summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/keys.rs187
-rw-r--r--src/lib.rs1
-rw-r--r--tests/data/keys/testy-broken-no-pk.pgpbin0 -> 966 bytes
-rw-r--r--tests/data/keys/testy-broken-no-sig-on-subkey.pgpbin0 -> 925 bytes
-rw-r--r--tests/data/keys/testy-broken-no-uid.pgpbin0 -> 857 bytes
-rw-r--r--tests/data/keys/testy-no-subkey.pgpbin0 -> 653 bytes
-rw-r--r--tests/data/keys/testy.pgpbin0 -> 1238 bytes
7 files changed, 188 insertions, 0 deletions
diff --git a/src/keys.rs b/src/keys.rs
new file mode 100644
index 00000000..cb21bb48
--- /dev/null
+++ b/src/keys.rs
@@ -0,0 +1,187 @@
+use super::openpgp;
+use super::openpgp::Packet;
+
+/// This represents a transferable public key (see [RFC 4880, section
+/// 11.1](https://tools.ietf.org/html/rfc4880#section-11.1)).
+#[derive(Debug)]
+pub struct TPK {
+ userids: Vec<UserIDBinding>,
+ subkeys: Vec<SubkeyBinding>,
+}
+
+#[derive(Debug)]
+pub struct SubkeyBinding {
+ subkey: openpgp::Key,
+ signatures: Vec<openpgp::Signature>,
+}
+
+#[derive(Debug)]
+pub struct UserIDBinding {
+ userid: openpgp::UserID,
+ signatures: Vec<openpgp::Signature>,
+}
+
+#[derive(Debug)]
+enum States {
+ Start,
+ TPK,
+ UserID(UserIDBinding),
+ Subkey(SubkeyBinding),
+ End,
+}
+
+impl TPK {
+ /// Return the first transferable public key found in `m`.
+ pub fn from_message(m: openpgp::Message) -> Option<Self> {
+ let mut state = States::Start;
+ let mut tpk = TPK { userids: vec![], subkeys: vec![] };
+ for p in m.into_iter() {
+ state = match state {
+ States::Start => {
+ /* Find the first public key packet. */
+ match p {
+ Packet::PublicKey(pk) => {
+ tpk.subkeys.push(SubkeyBinding{subkey: pk, signatures: vec![]});
+ States::TPK
+ },
+ _ => States::Start,
+ }
+ },
+ States::TPK => {
+ /* Find user id, or subkey packets. */
+ match p {
+ Packet::PublicKey(_pk) => {
+ States::End
+ },
+ Packet::UserID(uid) => {
+ States::UserID(UserIDBinding{userid: uid, signatures: vec![]})
+ },
+ Packet::PublicSubkey(key) => {
+ States::Subkey(SubkeyBinding{subkey: key, signatures: vec![]})
+ },
+ _ => States::TPK,
+ }
+ },
+ States::UserID(mut u) => {
+ /* Find signature packets. */
+ match p {
+ Packet::PublicKey(_pk) => {
+ States::End
+ },
+ Packet::UserID(uid) => {
+ tpk.userids.push(u);
+ States::UserID(UserIDBinding{userid: uid, signatures: vec![]})
+ },
+ Packet::PublicSubkey(key) => {
+ tpk.userids.push(u);
+ States::Subkey(SubkeyBinding{subkey: key, signatures: vec![]})
+ },
+ Packet::Signature(sig) => {
+ u.signatures.push(sig);
+ States::UserID(u)
+ },
+ _ => States::UserID(u),
+ }
+ },
+ States::Subkey(mut s) => {
+ /* Find signature packets. */
+ match p {
+ Packet::PublicKey(_pk) => {
+ States::End
+ },
+ Packet::UserID(uid) => {
+ tpk.subkeys.push(s);
+ States::UserID(UserIDBinding{userid: uid, signatures: vec![]})
+ },
+ Packet::PublicSubkey(key) => {
+ tpk.subkeys.push(s);
+ States::Subkey(SubkeyBinding{subkey: key, signatures: vec![]})
+ },
+ Packet::Signature(sig) => {
+ s.signatures.push(sig);
+ States::Subkey(s)
+ },
+ _ => States::Subkey(s),
+ }
+ },
+ States::End => break,
+ };
+ }
+
+ match state {
+ States::UserID(u) => {
+ tpk.userids.push(u);
+ Some(tpk)
+ },
+ States::Subkey(s) => {
+ tpk.subkeys.push(s);
+ Some(tpk)
+ },
+ States::End => Some(tpk),
+ _ => None,
+ }.and_then(|tpk| tpk.canonicalize())
+ }
+
+ fn canonicalize(self) -> Option<Self> {
+ // Sanity checks.
+
+ // - One or more User ID packets.
+ if self.userids.len() == 0 {
+ return None;
+ }
+
+ // - After each Subkey packet, one Signature packet.
+ for subkey in self.subkeys.iter().skip(1) {
+ if subkey.signatures.len() == 0 {
+ return None;
+ }
+ }
+
+ // XXX Do some canonicalization.
+
+ Some(self)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::TPK;
+ use super::openpgp;
+
+ macro_rules! bytes {
+ ( $x:expr ) => { include_bytes!(concat!("../tests/data/keys/", $x)) };
+ }
+
+ #[test]
+ fn broken() {
+ let m = openpgp::Message::from_bytes(bytes!("testy-broken-no-pk.pgp")).unwrap();
+ let tpk = TPK::from_message(m);
+ assert!(tpk.is_none());
+
+ let m = openpgp::Message::from_bytes(bytes!("testy-broken-no-uid.pgp")).unwrap();
+ let tpk = TPK::from_message(m);
+ assert!(tpk.is_none());
+
+ let m = openpgp::Message::from_bytes(bytes!("testy-broken-no-sig-on-subkey.pgp")).unwrap();
+ let tpk = TPK::from_message(m);
+ assert!(tpk.is_none());
+ }
+
+ #[test]
+ fn basics() {
+ let m = openpgp::Message::from_bytes(bytes!("testy.pgp")).unwrap();
+ let tpk = TPK::from_message(m).unwrap();
+ //println!("{:?}", tpk);
+
+ assert_eq!(tpk.userids.len(), 1, "number of userids");
+ // XXX .value is private
+ //assert_eq!(tpk.userids[0].userid.value, "Testy McTestface <testy@example.org>");
+ assert_eq!(tpk.subkeys.len(), 2, "number of subkeys");
+
+ let m = openpgp::Message::from_bytes(bytes!("testy-no-subkey.pgp")).unwrap();
+ let tpk = TPK::from_message(m).unwrap();
+
+ assert_eq!(tpk.userids.len(), 1, "number of userids");
+ assert_eq!(tpk.subkeys.len(), 1, "number of subkeys");
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 89507863..a69113c7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,5 +11,6 @@ extern crate flate2;
extern crate bzip2;
pub mod openpgp;
+pub mod keys;
pub mod key_store;
pub mod net;
diff --git a/tests/data/keys/testy-broken-no-pk.pgp b/tests/data/keys/testy-broken-no-pk.pgp
new file mode 100644
index 00000000..13cc0036
--- /dev/null
+++ b/tests/data/keys/testy-broken-no-pk.pgp
Binary files differ
diff --git a/tests/data/keys/testy-broken-no-sig-on-subkey.pgp b/tests/data/keys/testy-broken-no-sig-on-subkey.pgp
new file mode 100644
index 00000000..aa33a88d
--- /dev/null
+++ b/tests/data/keys/testy-broken-no-sig-on-subkey.pgp
Binary files differ
diff --git a/tests/data/keys/testy-broken-no-uid.pgp b/tests/data/keys/testy-broken-no-uid.pgp
new file mode 100644
index 00000000..28e978bd
--- /dev/null
+++ b/tests/data/keys/testy-broken-no-uid.pgp
Binary files differ
diff --git a/tests/data/keys/testy-no-subkey.pgp b/tests/data/keys/testy-no-subkey.pgp
new file mode 100644
index 00000000..8601d57f
--- /dev/null
+++ b/tests/data/keys/testy-no-subkey.pgp
Binary files differ
diff --git a/tests/data/keys/testy.pgp b/tests/data/keys/testy.pgp
new file mode 100644
index 00000000..60a61c82
--- /dev/null
+++ b/tests/data/keys/testy.pgp
Binary files differ