mirror of
https://gitlab.com/keys.openpgp.org/hagrid.git
synced 2025-10-06 00:23:08 +02:00
debug: add /debug to display key debug info
This commit is contained in:
16
dist/assets/site.css
vendored
16
dist/assets/site.css
vendored
@@ -223,6 +223,22 @@ span.email {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.debug_link {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
margin: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.debug_link a {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.debug_link a:hover {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.attribution {
|
||||
position: fixed;
|
||||
z-index: -1;
|
||||
|
3
dist/templates/found.html.hbs
vendored
3
dist/templates/found.html.hbs
vendored
@@ -13,5 +13,8 @@
|
||||
<span class="brand">keys.openpgp.org</span> from your OpenPGP software.<br />
|
||||
Take a look at our <a href="/about/usage">usage guide</a> for details.
|
||||
</p>
|
||||
<div class="debug_link">
|
||||
<a href="{{base_uri}}/debug?q={{ fpr }}">debug info</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/layout}}
|
||||
|
@@ -120,6 +120,10 @@ location /upload {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
||||
location /debug {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
||||
# explicitly cache the home directory
|
||||
location = / {
|
||||
proxy_cache static_cache;
|
||||
|
826
src/dump.rs
Normal file
826
src/dump.rs
Normal file
@@ -0,0 +1,826 @@
|
||||
// from https://gitlab.com/sequoia-pgp/dump.sequoia-pgp.org/blob/master/src/dump.rs
|
||||
// plus *very* slight adaptions for seqoia 0.8
|
||||
|
||||
use std::io::{self, Read};
|
||||
|
||||
use sequoia_openpgp::constants::SymmetricAlgorithm;
|
||||
use sequoia_openpgp::conversions::hex;
|
||||
use sequoia_openpgp::{Packet, Result};
|
||||
use sequoia_openpgp::packet::ctb::CTB;
|
||||
use sequoia_openpgp::packet::{Header, BodyLength, Signature};
|
||||
use sequoia_openpgp::packet::signature::subpacket::{Subpacket, SubpacketValue};
|
||||
use sequoia_openpgp::crypto::{SessionKey, s2k::S2K};
|
||||
use sequoia_openpgp::parse::{map::Map, Parse, PacketParserResult};
|
||||
|
||||
const TIMEFMT: &'static str = "%Y-%m-%dT%H:%M";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Kind {
|
||||
Message {
|
||||
encrypted: bool,
|
||||
},
|
||||
Keyring,
|
||||
TPK,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub fn dump(input: &mut dyn io::Read, output: &mut dyn io::Write, mpis: bool, hex: bool,
|
||||
sk: Option<&SessionKey>)
|
||||
-> Result<Kind> {
|
||||
let mut ppr
|
||||
= sequoia_openpgp::parse::PacketParserBuilder::from_reader(input)?
|
||||
.map(hex).finalize()?;
|
||||
let mut message_encrypted = false;
|
||||
let width = 32 * 4 + 80;
|
||||
let mut dumper = PacketDumper::new(width, mpis);
|
||||
|
||||
while let PacketParserResult::Some(mut pp) = ppr {
|
||||
let additional_fields = match pp.packet {
|
||||
Packet::Literal(_) => {
|
||||
let mut prefix = vec![0; 40];
|
||||
let n = pp.read(&mut prefix)?;
|
||||
Some(vec![
|
||||
format!("Content: {:?}{}",
|
||||
String::from_utf8_lossy(&prefix[..n]),
|
||||
if n == prefix.len() { "..." } else { "" }),
|
||||
])
|
||||
},
|
||||
Packet::SEIP(_) if sk.is_none() => {
|
||||
message_encrypted = true;
|
||||
Some(vec!["No session key supplied".into()])
|
||||
}
|
||||
Packet::SEIP(_) if sk.is_some() => {
|
||||
message_encrypted = true;
|
||||
let sk = sk.as_ref().unwrap();
|
||||
let mut decrypted_with = None;
|
||||
for algo in 1..20 {
|
||||
let algo = SymmetricAlgorithm::from(algo);
|
||||
if let Ok(size) = algo.key_size() {
|
||||
if size != sk.len() { continue; }
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(_) = pp.decrypt(algo, sk) {
|
||||
decrypted_with = Some(algo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mut fields = Vec::new();
|
||||
fields.push(format!("Session key: {}", hex::encode(sk)));
|
||||
if let Some(algo) = decrypted_with {
|
||||
fields.push(format!("Symmetric algo: {}", algo));
|
||||
fields.push("Decryption successful".into());
|
||||
} else {
|
||||
fields.push("Decryption failed".into());
|
||||
}
|
||||
Some(fields)
|
||||
},
|
||||
Packet::AED(_) if sk.is_none() => {
|
||||
message_encrypted = true;
|
||||
Some(vec!["No session key supplied".into()])
|
||||
}
|
||||
Packet::AED(_) if sk.is_some() => {
|
||||
message_encrypted = true;
|
||||
let sk = sk.as_ref().unwrap();
|
||||
let algo = if let Packet::AED(ref aed) = pp.packet {
|
||||
aed.symmetric_algo()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let _ = pp.decrypt(algo, sk);
|
||||
|
||||
let mut fields = Vec::new();
|
||||
fields.push(format!("Session key: {}", hex::encode(sk)));
|
||||
if pp.decrypted() {
|
||||
fields.push("Decryption successful".into());
|
||||
} else {
|
||||
fields.push("Decryption failed".into());
|
||||
}
|
||||
Some(fields)
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let header = pp.header().clone();
|
||||
let map = pp.take_map();
|
||||
|
||||
let (packet, ppr_) = pp.recurse()?;
|
||||
ppr = ppr_;
|
||||
let recursion_depth = ppr.last_recursion_depth().unwrap();
|
||||
|
||||
dumper.packet(output, recursion_depth as usize,
|
||||
header, packet, map, additional_fields)?;
|
||||
}
|
||||
|
||||
dumper.flush(output)?;
|
||||
|
||||
if let PacketParserResult::EOF(eof) = ppr {
|
||||
if eof.is_message().is_ok() {
|
||||
Ok(Kind::Message {
|
||||
encrypted: message_encrypted,
|
||||
})
|
||||
} else if eof.is_tpk().is_ok() {
|
||||
Ok(Kind::TPK)
|
||||
} else if eof.is_keyring().is_ok() {
|
||||
Ok(Kind::Keyring)
|
||||
} else {
|
||||
Ok(Kind::Unknown)
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
struct Node {
|
||||
header: Header,
|
||||
packet: Packet,
|
||||
map: Option<Map>,
|
||||
additional_fields: Option<Vec<String>>,
|
||||
children: Vec<Node>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn new(header: Header, packet: Packet, map: Option<Map>,
|
||||
additional_fields: Option<Vec<String>>) -> Self {
|
||||
Node {
|
||||
header: header,
|
||||
packet: packet,
|
||||
map: map,
|
||||
additional_fields: additional_fields,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn append(&mut self, depth: usize, node: Node) {
|
||||
if depth == 0 {
|
||||
self.children.push(node);
|
||||
} else {
|
||||
self.children.iter_mut().last().unwrap().append(depth - 1, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PacketDumper {
|
||||
width: usize,
|
||||
mpis: bool,
|
||||
root: Option<Node>,
|
||||
}
|
||||
|
||||
impl PacketDumper {
|
||||
pub fn new(width: usize, mpis: bool) -> Self {
|
||||
PacketDumper {
|
||||
width: width,
|
||||
mpis: mpis,
|
||||
root: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn packet(&mut self, output: &mut dyn io::Write, depth: usize,
|
||||
header: Header, p: Packet, map: Option<Map>,
|
||||
additional_fields: Option<Vec<String>>)
|
||||
-> Result<()> {
|
||||
let node = Node::new(header, p, map, additional_fields);
|
||||
if self.root.is_none() {
|
||||
assert_eq!(depth, 0);
|
||||
self.root = Some(node);
|
||||
} else {
|
||||
if depth == 0 {
|
||||
let root = self.root.take().unwrap();
|
||||
self.dump_tree(output, "", &root)?;
|
||||
self.root = Some(node);
|
||||
} else {
|
||||
self.root.as_mut().unwrap().append(depth - 1, node);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&self, output: &mut dyn io::Write) -> Result<()> {
|
||||
if let Some(root) = self.root.as_ref() {
|
||||
self.dump_tree(output, "", &root)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_tree(&self, output: &mut dyn io::Write, indent: &str, node: &Node)
|
||||
-> Result<()> {
|
||||
let indent_node =
|
||||
format!("{}{} ", indent,
|
||||
if node.children.is_empty() { " " } else { "│" });
|
||||
self.dump_packet(output, &indent_node, Some(&node.header), &node.packet,
|
||||
node.map.as_ref(), node.additional_fields.as_ref())?;
|
||||
if node.children.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let last = node.children.len() - 1;
|
||||
for (i, child) in node.children.iter().enumerate() {
|
||||
let is_last = i == last;
|
||||
write!(output, "{}{}── ", indent,
|
||||
if is_last { "└" } else { "├" })?;
|
||||
let indent_child =
|
||||
format!("{}{} ", indent,
|
||||
if is_last { " " } else { "│" });
|
||||
self.dump_tree(output, &indent_child, child)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_packet(&self, output: &mut dyn io::Write, i: &str,
|
||||
header: Option<&Header>, p: &Packet, map: Option<&Map>,
|
||||
additional_fields: Option<&Vec<String>>)
|
||||
-> Result<()> {
|
||||
use sequoia_openpgp::Packet::*;
|
||||
|
||||
if let Some(h) = header {
|
||||
write!(output, "{} CTB, {}: ",
|
||||
if let CTB::Old(_) = h.ctb { "Old" } else { "New" },
|
||||
match h.length {
|
||||
BodyLength::Full(n) =>
|
||||
format!("{} bytes", n),
|
||||
BodyLength::Partial(n) =>
|
||||
format!("partial length, {} bytes in first chunk", n),
|
||||
BodyLength::Indeterminate =>
|
||||
"indeterminate length".into(),
|
||||
})?;
|
||||
}
|
||||
|
||||
match p {
|
||||
Unknown(ref u) => {
|
||||
writeln!(output, "Unknown Packet")?;
|
||||
writeln!(output, "{} Tag: {}", i, u.tag())?;
|
||||
writeln!(output, "{} Error: {}", i, u.error())?;
|
||||
},
|
||||
|
||||
Signature(ref s) => {
|
||||
writeln!(output, "Signature Packet")?;
|
||||
writeln!(output, "{} Version: {}", i, s.version())?;
|
||||
writeln!(output, "{} Type: {}", i, s.sigtype())?;
|
||||
writeln!(output, "{} Pk algo: {}", i, s.pk_algo())?;
|
||||
writeln!(output, "{} Hash algo: {}", i, s.hash_algo())?;
|
||||
if s.hashed_area().iter().count() > 0 {
|
||||
writeln!(output, "{} Hashed area:", i)?;
|
||||
for (_, _, pkt) in s.hashed_area().iter() {
|
||||
self.dump_subpacket(output, i, pkt, s)?;
|
||||
}
|
||||
}
|
||||
if s.unhashed_area().iter().count() > 0 {
|
||||
writeln!(output, "{} Unhashed area:", i)?;
|
||||
for (_, _, pkt) in s.unhashed_area().iter() {
|
||||
self.dump_subpacket(output, i, pkt, s)?;
|
||||
}
|
||||
}
|
||||
writeln!(output, "{} Hash prefix: {}", i,
|
||||
hex::encode(s.hash_prefix()))?;
|
||||
write!(output, "{} Level: {} ", i, s.level())?;
|
||||
match s.level() {
|
||||
0 => writeln!(output, "(signature over data)")?,
|
||||
1 => writeln!(output, "(notarization over signatures \
|
||||
level 0 and data)")?,
|
||||
n => writeln!(output, "(notarization over signatures \
|
||||
level <= {} and data)", n - 1)?,
|
||||
}
|
||||
if self.mpis {
|
||||
use sequoia_openpgp::crypto::mpis::Signature::*;
|
||||
writeln!(output, "{}", i)?;
|
||||
writeln!(output, "{} Signature:", i)?;
|
||||
|
||||
let ii = format!("{} ", i);
|
||||
match s.mpis() {
|
||||
RSA { s } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&s.value],
|
||||
&["s"])?,
|
||||
DSA { r, s } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&r.value, &s.value],
|
||||
&["r", "s"])?,
|
||||
Elgamal { r, s } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&r.value, &s.value],
|
||||
&["r", "s"])?,
|
||||
EdDSA { r, s } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&r.value, &s.value],
|
||||
&["r", "s"])?,
|
||||
ECDSA { r, s } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&r.value, &s.value],
|
||||
&["r", "s"])?,
|
||||
Unknown { mpis, rest } => {
|
||||
let keys: Vec<String> =
|
||||
(0..mpis.len()).map(
|
||||
|i| format!("mpi{}", i)).collect();
|
||||
self.dump_mpis(
|
||||
output, &ii,
|
||||
&mpis.iter().map(|m| m.value.iter().as_slice())
|
||||
.collect::<Vec<_>>()[..],
|
||||
&keys.iter().map(|k| k.as_str())
|
||||
.collect::<Vec<_>>()[..],
|
||||
)?;
|
||||
|
||||
self.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
OnePassSig(ref o) => {
|
||||
writeln!(output, "One-Pass Signature Packet")?;
|
||||
writeln!(output, "{} Version: {}", i, o.version())?;
|
||||
writeln!(output, "{} Type: {}", i, o.sigtype())?;
|
||||
writeln!(output, "{} Pk algo: {}", i, o.pk_algo())?;
|
||||
writeln!(output, "{} Hash algo: {}", i, o.hash_algo())?;
|
||||
writeln!(output, "{} Issuer: {}", i, o.issuer())?;
|
||||
writeln!(output, "{} Last: {}", i, o.last())?;
|
||||
},
|
||||
|
||||
PublicKey(ref k) | PublicSubkey(ref k)
|
||||
| SecretKey(ref k) | SecretSubkey(ref k) =>
|
||||
{
|
||||
writeln!(output, "{}", p.tag())?;
|
||||
writeln!(output, "{} Version: {}", i, k.version())?;
|
||||
writeln!(output, "{} Creation time: {}", i,
|
||||
time::strftime(TIMEFMT, k.creation_time()).unwrap())?;
|
||||
writeln!(output, "{} Pk algo: {}", i, k.pk_algo())?;
|
||||
if let Some(bits) = k.mpis().bits() {
|
||||
writeln!(output, "{} Pk size: {} bits", i, bits)?;
|
||||
}
|
||||
if self.mpis {
|
||||
use sequoia_openpgp::crypto::mpis::PublicKey::*;
|
||||
writeln!(output, "{}", i)?;
|
||||
writeln!(output, "{} Public Key:", i)?;
|
||||
|
||||
let ii = format!("{} ", i);
|
||||
match k.mpis() {
|
||||
RSA { e, n } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&e.value, &n.value],
|
||||
&["e", "n"])?,
|
||||
DSA { p, q, g, y } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&p.value, &q.value, &g.value,
|
||||
&y.value],
|
||||
&["p", "q", "g", "y"])?,
|
||||
Elgamal { p, g, y } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&p.value, &g.value, &y.value],
|
||||
&["p", "g", "y"])?,
|
||||
EdDSA { curve, q } => {
|
||||
writeln!(output, "{} Curve: {}", ii, curve)?;
|
||||
self.dump_mpis(output, &ii, &[&q.value], &["q"])?;
|
||||
},
|
||||
ECDSA { curve, q } => {
|
||||
writeln!(output, "{} Curve: {}", ii, curve)?;
|
||||
self.dump_mpis(output, &ii, &[&q.value], &["q"])?;
|
||||
},
|
||||
ECDH { curve, q, hash, sym } => {
|
||||
writeln!(output, "{} Curve: {}", ii, curve)?;
|
||||
writeln!(output, "{} Hash algo: {}", ii, hash)?;
|
||||
writeln!(output, "{} Symmetric algo: {}", ii,
|
||||
sym)?;
|
||||
self.dump_mpis(output, &ii, &[&q.value], &["q"])?;
|
||||
},
|
||||
Unknown { mpis, rest } => {
|
||||
let keys: Vec<String> =
|
||||
(0..mpis.len()).map(
|
||||
|i| format!("mpi{}", i)).collect();
|
||||
self.dump_mpis(
|
||||
output, &ii,
|
||||
&mpis.iter().map(|m| m.value.iter().as_slice())
|
||||
.collect::<Vec<_>>()[..],
|
||||
&keys.iter().map(|k| k.as_str())
|
||||
.collect::<Vec<_>>()[..],
|
||||
)?;
|
||||
|
||||
self.dump_mpis(output, &ii, &[&rest[..]], &["rest"])?;
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(secrets) = k.secret() {
|
||||
use sequoia_openpgp::crypto::mpis::SecretKey::*;
|
||||
writeln!(output, "{}", i)?;
|
||||
writeln!(output, "{} Secret Key:", i)?;
|
||||
|
||||
let ii = format!("{} ", i);
|
||||
match secrets {
|
||||
sequoia_openpgp::packet::key::SecretKey::Unencrypted {
|
||||
mpis,
|
||||
} => match mpis {
|
||||
RSA { d, p, q, u } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&d.value, &p.value, &q.value,
|
||||
&u.value],
|
||||
&["d", "p", "q", "u"])?,
|
||||
DSA { x } =>
|
||||
self.dump_mpis(output, &ii, &[&x.value],
|
||||
&["x"])?,
|
||||
Elgamal { x } =>
|
||||
self.dump_mpis(output, &ii, &[&x.value],
|
||||
&["x"])?,
|
||||
EdDSA { scalar } =>
|
||||
self.dump_mpis(output, &ii, &[&scalar.value],
|
||||
&["scalar"])?,
|
||||
ECDSA { scalar } =>
|
||||
self.dump_mpis(output, &ii, &[&scalar.value],
|
||||
&["scalar"])?,
|
||||
ECDH { scalar } =>
|
||||
self.dump_mpis(output, &ii, &[&scalar.value],
|
||||
&["scalar"])?,
|
||||
Unknown { mpis, rest } => {
|
||||
let keys: Vec<String> =
|
||||
(0..mpis.len()).map(
|
||||
|i| format!("mpi{}", i)).collect();
|
||||
self.dump_mpis(
|
||||
output, &ii,
|
||||
&mpis.iter()
|
||||
.map(|m| m.value.iter().as_slice())
|
||||
.collect::<Vec<_>>()[..],
|
||||
&keys.iter().map(|k| k.as_str())
|
||||
.collect::<Vec<_>>()[..],
|
||||
)?;
|
||||
|
||||
self.dump_mpis(output, &ii, &[rest],
|
||||
&["rest"])?;
|
||||
},
|
||||
},
|
||||
sequoia_openpgp::packet::key::SecretKey::Encrypted {
|
||||
s2k, algorithm, ciphertext,
|
||||
} => {
|
||||
writeln!(output, "{}", i)?;
|
||||
write!(output, "{} S2K: ", ii)?;
|
||||
self.dump_s2k(output, &ii, s2k)?;
|
||||
writeln!(output, "{} Sym. algo: {}", ii,
|
||||
algorithm)?;
|
||||
self.dump_mpis(output, &ii, &[&ciphertext[..]],
|
||||
&["ciphertext"])?;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Trust(ref p) => {
|
||||
writeln!(output, "Trust Packet")?;
|
||||
writeln!(output, "{} Value: {}", i, hex::encode(p.value()))?;
|
||||
},
|
||||
|
||||
UserID(ref u) => {
|
||||
writeln!(output, "User ID Packet")?;
|
||||
writeln!(output, "{} Value: {}", i,
|
||||
String::from_utf8_lossy(u.value()))?;
|
||||
},
|
||||
|
||||
UserAttribute(ref u) => {
|
||||
use sequoia_openpgp::packet::user_attribute::{Subpacket, Image};
|
||||
writeln!(output, "User Attribute Packet")?;
|
||||
|
||||
for subpacket in u.subpackets() {
|
||||
match subpacket {
|
||||
Ok(Subpacket::Image(image)) => match image {
|
||||
Image::JPEG(data) =>
|
||||
writeln!(output, "{} JPEG: {} bytes", i,
|
||||
data.len())?,
|
||||
Image::Private(n, data) =>
|
||||
writeln!(output,
|
||||
"{} Private image({}): {} bytes", i,
|
||||
n, data.len())?,
|
||||
Image::Unknown(n, data) =>
|
||||
writeln!(output,
|
||||
"{} Unknown image({}): {} bytes", i,
|
||||
n, data.len())?,
|
||||
},
|
||||
Ok(Subpacket::Unknown(n, data)) =>
|
||||
writeln!(output,
|
||||
"{} Unknown subpacket({}): {} bytes", i,
|
||||
n, data.len())?,
|
||||
Err(e) =>
|
||||
writeln!(output,
|
||||
"{} Invalid subpacket encoding: {}", i,
|
||||
e)?,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Marker(_) => {
|
||||
writeln!(output, "Marker Packet")?;
|
||||
},
|
||||
|
||||
Literal(ref l) => {
|
||||
writeln!(output, "Literal Data Packet")?;
|
||||
writeln!(output, "{} Format: {}", i, l.format())?;
|
||||
if let Some(filename) = l.filename() {
|
||||
writeln!(output, "{} Filename: {}", i,
|
||||
String::from_utf8_lossy(filename))?;
|
||||
}
|
||||
if let Some(timestamp) = l.date() {
|
||||
writeln!(output, "{} Timestamp: {}", i,
|
||||
time::strftime(TIMEFMT, timestamp).unwrap())?;
|
||||
}
|
||||
},
|
||||
|
||||
CompressedData(ref c) => {
|
||||
writeln!(output, "Compressed Data Packet")?;
|
||||
writeln!(output, "{} Algorithm: {}", i, c.algorithm())?;
|
||||
},
|
||||
|
||||
PKESK(ref p) => {
|
||||
writeln!(output, "Public-key Encrypted Session Key Packet")?;
|
||||
writeln!(output, "{} Version: {}", i, p.version())?;
|
||||
writeln!(output, "{} Recipient: {}", i, p.recipient())?;
|
||||
writeln!(output, "{} Pk algo: {}", i, p.pk_algo())?;
|
||||
if self.mpis {
|
||||
use sequoia_openpgp::crypto::mpis::Ciphertext::*;
|
||||
writeln!(output, "{}", i)?;
|
||||
writeln!(output, "{} Encrypted session key:", i)?;
|
||||
|
||||
let ii = format!("{} ", i);
|
||||
match p.esk() {
|
||||
RSA { c } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&c.value],
|
||||
&["c"])?,
|
||||
Elgamal { e, c } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&e.value, &c.value],
|
||||
&["e", "c"])?,
|
||||
ECDH { e, key } =>
|
||||
self.dump_mpis(output, &ii,
|
||||
&[&e.value, key],
|
||||
&["e", "key"])?,
|
||||
Unknown { mpis, rest } => {
|
||||
let keys: Vec<String> =
|
||||
(0..mpis.len()).map(
|
||||
|i| format!("mpi{}", i)).collect();
|
||||
self.dump_mpis(
|
||||
output, &ii,
|
||||
&mpis.iter().map(|m| m.value.iter().as_slice())
|
||||
.collect::<Vec<_>>()[..],
|
||||
&keys.iter().map(|k| k.as_str())
|
||||
.collect::<Vec<_>>()[..],
|
||||
)?;
|
||||
|
||||
self.dump_mpis(output, &ii, &[rest], &["rest"])?;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
SKESK(ref s) => {
|
||||
writeln!(output, "Symmetric-key Encrypted Session Key Packet")?;
|
||||
writeln!(output, "{} Version: {}", i, s.version())?;
|
||||
match s {
|
||||
sequoia_openpgp::packet::SKESK::V4(ref s) => {
|
||||
writeln!(output, "{} Symmetric algo: {}", i,
|
||||
s.symmetric_algo())?;
|
||||
write!(output, "{} S2K: ", i)?;
|
||||
self.dump_s2k(output, i, s.s2k())?;
|
||||
if let Some(esk) = s.esk() {
|
||||
writeln!(output, "{} ESK: {}", i,
|
||||
hex::encode(esk))?;
|
||||
}
|
||||
},
|
||||
|
||||
sequoia_openpgp::packet::SKESK::V5(ref s) => {
|
||||
writeln!(output, "{} Symmetric algo: {}", i,
|
||||
s.symmetric_algo())?;
|
||||
writeln!(output, "{} AEAD: {}", i,
|
||||
s.aead_algo())?;
|
||||
write!(output, "{} S2K: ", i)?;
|
||||
self.dump_s2k(output, i, s.s2k())?;
|
||||
writeln!(output, "{} IV: {}", i,
|
||||
hex::encode(s.aead_iv()))?;
|
||||
if let Some(esk) = s.esk() {
|
||||
writeln!(output, "{} ESK: {}", i,
|
||||
hex::encode(esk))?;
|
||||
}
|
||||
writeln!(output, "{} Digest: {}", i,
|
||||
hex::encode(s.aead_digest()))?;
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
SEIP(ref s) => {
|
||||
writeln!(output, "Encrypted and Integrity Protected Data Packet")?;
|
||||
writeln!(output, "{} Version: {}", i, s.version())?;
|
||||
},
|
||||
|
||||
MDC(ref m) => {
|
||||
writeln!(output, "Modification Detection Code Packet")?;
|
||||
writeln!(output, "{} Hash: {}",
|
||||
i, hex::encode(m.hash()))?;
|
||||
writeln!(output, "{} Computed hash: {}",
|
||||
i, hex::encode(m.computed_hash()))?;
|
||||
},
|
||||
|
||||
AED(ref a) => {
|
||||
writeln!(output, "AEAD Encrypted Data Packet")?;
|
||||
writeln!(output, "{} Version: {}", i, a.version())?;
|
||||
writeln!(output, "{} Symmetric algo: {}", i, a.symmetric_algo())?;
|
||||
writeln!(output, "{} AEAD: {}", i, a.aead())?;
|
||||
writeln!(output, "{} Chunk size: {}", i, a.chunk_size())?;
|
||||
writeln!(output, "{} IV: {}", i, hex::encode(a.iv()))?;
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(fields) = additional_fields {
|
||||
for field in fields {
|
||||
writeln!(output, "{} {}", i, field)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(map) = map {
|
||||
writeln!(output, "{}", i)?;
|
||||
let mut hd = hex::Dumper::new(output, self.indentation_for_hexdump(
|
||||
i, map.iter().map(|f| f.name.len()).max()
|
||||
.expect("we always have one entry")));
|
||||
|
||||
for field in map.iter() {
|
||||
hd.write(field.data, field.name)?;
|
||||
}
|
||||
|
||||
let output = hd.into_inner();
|
||||
writeln!(output, "{}", i)?;
|
||||
} else {
|
||||
writeln!(output, "{}", i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_subpacket(&self, output: &mut dyn io::Write, i: &str,
|
||||
s: Subpacket, sig: &Signature)
|
||||
-> Result<()> {
|
||||
use self::SubpacketValue::*;
|
||||
|
||||
match s.value {
|
||||
Unknown(ref b) =>
|
||||
write!(output, "{} Unknown: {:?}", i, b)?,
|
||||
Invalid(ref b) =>
|
||||
write!(output, "{} Invalid: {:?}", i, b)?,
|
||||
SignatureCreationTime(ref t) =>
|
||||
write!(output, "{} Signature creation time: {}", i,
|
||||
time::strftime(TIMEFMT, t).unwrap())?,
|
||||
SignatureExpirationTime(ref t) =>
|
||||
write!(output, "{} Signature expiration time: {} ({})",
|
||||
i, t,
|
||||
if let Some(creation) = sig.signature_creation_time() {
|
||||
time::strftime(TIMEFMT, &(creation + *t))
|
||||
.unwrap()
|
||||
} else {
|
||||
" (no Signature Creation Time subpacket)".into()
|
||||
})?,
|
||||
ExportableCertification(e) =>
|
||||
write!(output, "{} Exportable certification: {}", i, e)?,
|
||||
TrustSignature{level, trust} =>
|
||||
write!(output, "{} Trust signature: level {} trust {}", i,
|
||||
level, trust)?,
|
||||
RegularExpression(ref r) =>
|
||||
write!(output, "{} Regular expression: {}", i,
|
||||
String::from_utf8_lossy(r))?,
|
||||
Revocable(r) =>
|
||||
write!(output, "{} Revocable: {}", i, r)?,
|
||||
KeyExpirationTime(ref t) =>
|
||||
write!(output, "{} Key expiration time: {}", i, t)?,
|
||||
PreferredSymmetricAlgorithms(ref c) =>
|
||||
write!(output, "{} Symmetric algo preferences: {}", i,
|
||||
c.iter().map(|c| format!("{:?}", c))
|
||||
.collect::<Vec<String>>().join(", "))?,
|
||||
RevocationKey{class, pk_algo, ref fp} =>
|
||||
write!(output,
|
||||
"{} Revocation key: class {} algo {} fingerprint {}", i,
|
||||
class, pk_algo, fp)?,
|
||||
Issuer(ref is) =>
|
||||
write!(output, "{} Issuer: {}", i, is)?,
|
||||
NotationData(ref n) =>
|
||||
write!(output, "{} Notation: {:?}", i, n)?,
|
||||
PreferredHashAlgorithms(ref h) =>
|
||||
write!(output, "{} Hash preferences: {}", i,
|
||||
h.iter().map(|h| format!("{:?}", h))
|
||||
.collect::<Vec<String>>().join(", "))?,
|
||||
PreferredCompressionAlgorithms(ref c) =>
|
||||
write!(output, "{} Compression preferences: {}", i,
|
||||
c.iter().map(|c| format!("{:?}", c))
|
||||
.collect::<Vec<String>>().join(", "))?,
|
||||
KeyServerPreferences(ref p) =>
|
||||
write!(output, "{} Keyserver preferences: {:?}", i, p)?,
|
||||
PreferredKeyServer(ref k) =>
|
||||
write!(output, "{} Preferred keyserver: {}", i,
|
||||
String::from_utf8_lossy(k))?,
|
||||
PrimaryUserID(p) =>
|
||||
write!(output, "{} Primary User ID: {}", i, p)?,
|
||||
PolicyURI(ref p) =>
|
||||
write!(output, "{} Policy URI: {}", i,
|
||||
String::from_utf8_lossy(p))?,
|
||||
KeyFlags(ref k) =>
|
||||
write!(output, "{} Key flags: {:?}", i, k)?,
|
||||
SignersUserID(ref u) =>
|
||||
write!(output, "{} Signer's User ID: {}", i,
|
||||
String::from_utf8_lossy(u))?,
|
||||
ReasonForRevocation{code, ref reason} => {
|
||||
let reason = String::from_utf8_lossy(reason);
|
||||
write!(output, "{} Reason for revocation: {}{}{}", i, code,
|
||||
if reason.len() > 0 { ", " } else { "" }, reason)?
|
||||
}
|
||||
Features(ref f) =>
|
||||
write!(output, "{} Features: {:?}", i, f)?,
|
||||
SignatureTarget{pk_algo, hash_algo, ref digest} =>
|
||||
write!(output, "{} Signature target: {}, {}, {}", i,
|
||||
pk_algo, hash_algo, hex::encode(digest))?,
|
||||
EmbeddedSignature(_) =>
|
||||
// Embedded signature is dumped below.
|
||||
write!(output, "{} Embedded signature: ", i)?,
|
||||
IssuerFingerprint(ref fp) =>
|
||||
write!(output, "{} Issuer Fingerprint: {}", i, fp)?,
|
||||
PreferredAEADAlgorithms(ref c) =>
|
||||
write!(output, "{} AEAD preferences: {}", i,
|
||||
c.iter().map(|c| format!("{:?}", c))
|
||||
.collect::<Vec<String>>().join(", "))?,
|
||||
IntendedRecipient(ref fp) =>
|
||||
write!(output, "{} Intended Recipient: {}", i, fp)?,
|
||||
}
|
||||
|
||||
if s.critical {
|
||||
write!(output, " (critical)")?;
|
||||
}
|
||||
writeln!(output)?;
|
||||
|
||||
match s.value {
|
||||
EmbeddedSignature(ref sig) => {
|
||||
let indent = format!("{} ", i);
|
||||
self.dump_packet(output, &indent, None, sig, None, None)?;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_s2k(&self, output: &mut dyn io::Write, i: &str, s2k: &S2K)
|
||||
-> Result<()> {
|
||||
use self::S2K::*;
|
||||
match s2k {
|
||||
Simple { hash } => {
|
||||
writeln!(output, "Simple")?;
|
||||
writeln!(output, "{} Hash: {}", i, hash)?;
|
||||
},
|
||||
Salted { hash, ref salt } => {
|
||||
writeln!(output, "Salted")?;
|
||||
writeln!(output, "{} Hash: {}", i, hash)?;
|
||||
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
|
||||
},
|
||||
Iterated { hash, ref salt, .. } => {
|
||||
writeln!(output, "Iterated")?;
|
||||
writeln!(output, "{} Hash: {}", i, hash)?;
|
||||
writeln!(output, "{} Salt: {}", i, hex::encode(salt))?;
|
||||
// writeln!(output, "{} Iterations: {}", i, iterations)?;
|
||||
},
|
||||
Private(n) =>
|
||||
writeln!(output, "Private({})", n)?,
|
||||
Unknown(n) =>
|
||||
writeln!(output, "Unknown({})", n)?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dump_mpis(&self, output: &mut dyn io::Write, i: &str,
|
||||
chunks: &[&[u8]], keys: &[&str]) -> Result<()> {
|
||||
assert_eq!(chunks.len(), keys.len());
|
||||
if chunks.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let max_key_len = keys.iter().map(|k| k.len()).max().unwrap();
|
||||
|
||||
for (chunk, key) in chunks.iter().zip(keys.iter()) {
|
||||
writeln!(output, "{}", i)?;
|
||||
let mut hd = hex::Dumper::new(
|
||||
Vec::new(), self.indentation_for_hexdump(i, max_key_len));
|
||||
hd.write(*chunk, *key)?;
|
||||
output.write_all(&hd.into_inner())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns indentation for hex dumps.
|
||||
///
|
||||
/// Returns a prefix of `i` so that a hexdump with labels no
|
||||
/// longer than `max_label_len` will fit into the target width.
|
||||
fn indentation_for_hexdump(&self, i: &str, max_label_len: usize) -> String {
|
||||
let amount = ::std::cmp::max(
|
||||
0,
|
||||
::std::cmp::min(
|
||||
self.width as isize
|
||||
- 63 // Length of address, hex digits, and whitespace.
|
||||
- max_label_len as isize,
|
||||
i.len() as isize),
|
||||
) as usize;
|
||||
|
||||
format!("{} ", &i.chars().take(amount).collect::<String>())
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -35,6 +35,7 @@ mod web;
|
||||
mod tokens;
|
||||
mod sealed_state;
|
||||
mod rate_limiter;
|
||||
mod dump;
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = web::serve() {
|
||||
|
44
src/web/debug_web.rs
Normal file
44
src/web/debug_web.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use std::io;
|
||||
|
||||
use dump::{self, Kind};
|
||||
use web::MyResponse;
|
||||
|
||||
use database::{Database, KeyDatabase, Query};
|
||||
|
||||
#[get("/debug?<q>")]
|
||||
pub fn debug_info(
|
||||
db: rocket::State<KeyDatabase>,
|
||||
q: String,
|
||||
) -> MyResponse {
|
||||
let query = match q.parse::<Query>() {
|
||||
Ok(query) => query,
|
||||
Err(_) => return MyResponse::bad_request_plain("bad request"),
|
||||
};
|
||||
let fp = match db.lookup_primary_fingerprint(&query) {
|
||||
Some(fp) => fp,
|
||||
None => return MyResponse::not_found_plain(query.describe_error()),
|
||||
};
|
||||
|
||||
let armored_key = match db.by_fpr(&fp) {
|
||||
Some(armored_key) => armored_key,
|
||||
None => return MyResponse::not_found_plain(query.describe_error()),
|
||||
};
|
||||
|
||||
let mut result = Vec::new();
|
||||
let dump_result = dump::dump(
|
||||
&mut io::Cursor::new(armored_key.as_bytes()),
|
||||
&mut result,
|
||||
false,
|
||||
false,
|
||||
None);
|
||||
match dump_result {
|
||||
Ok(Kind::TPK) => {
|
||||
match String::from_utf8(result) {
|
||||
Ok(dump_text) => MyResponse::plain(dump_text),
|
||||
Err(e) => MyResponse::ise(e.into()),
|
||||
}
|
||||
},
|
||||
Ok(_) => MyResponse::ise(failure::err_msg("Internal parsing error!")),
|
||||
Err(e) => MyResponse::ise(e),
|
||||
}
|
||||
}
|
@@ -29,6 +29,7 @@ mod maintenance;
|
||||
mod vks;
|
||||
mod vks_web;
|
||||
mod vks_api;
|
||||
mod debug_web;
|
||||
|
||||
use web::maintenance::MaintenanceMode;
|
||||
|
||||
@@ -337,6 +338,8 @@ fn rocket_factory(rocket: rocket::Rocket) -> Result<rocket::Rocket> {
|
||||
vks_web::verify_confirm,
|
||||
vks_web::quick_upload,
|
||||
vks_web::quick_upload_proceed,
|
||||
// Debug
|
||||
debug_web::debug_info,
|
||||
// HKP
|
||||
hkp::pks_lookup,
|
||||
hkp::pks_add_form,
|
||||
|
Reference in New Issue
Block a user