From d1c5e37b90fb8c3ac4221f77078ecd80bd9bc7f7 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 11 Mar 2011 02:34:36 -0500 Subject: [PATCH] Crypt::Monkeysphere::OpenPGP : bring in pieces of keytrans for fingerprint computation --- Changelog | 4 +- Crypt/Monkeysphere/OpenPGP.pm | 186 +++++++++++++++++++++++++++++++ unit-tests/30.fingerprints/fpr.t | 18 +++ 3 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 Crypt/Monkeysphere/OpenPGP.pm create mode 100644 unit-tests/30.fingerprints/fpr.t diff --git a/Changelog b/Changelog index 5eef10a..ba076ba 100644 --- a/Changelog +++ b/Changelog @@ -13,7 +13,9 @@ msva-perl (0.9~pre) upstream; - unit tests and unit test harness [ Daniel Kahn Gillmor ] - * now depending on Crypt::X509 0.50 for pubkey components directly. + * Now depending on Crypt::X509 0.50 for pubkey components directly. + * Crypt::Monkeysphere::OpenPGP for helper functions in + packet generation and parsing. -- Daniel Kahn Gillmor Fri, 11 Mar 2011 01:24:55 -0500 diff --git a/Crypt/Monkeysphere/OpenPGP.pm b/Crypt/Monkeysphere/OpenPGP.pm new file mode 100644 index 0000000..59dbcc9 --- /dev/null +++ b/Crypt/Monkeysphere/OpenPGP.pm @@ -0,0 +1,186 @@ +package Crypt::Monkeysphere::OpenPGP; + +use strict; +use warnings; + +use Math::BigInt; +use Digest::SHA; + +## WARNING! This entire module has an unstable API at the moment. +## Please do not rely on it, as it may change in the near future. + + +my $tables = { + # see RFC 4880 section 9.1 (ignoring deprecated algorithms for now) + asym_algos => { rsa => 1, + elgamal => 16, + dsa => 17, + }, + + # see RFC 4880 section 9.2 + ciphers => { plaintext => 0, + idea => 1, + tripledes => 2, + cast5 => 3, + blowfish => 4, + aes128 => 7, + aes192 => 8, + aes256 => 9, + twofish => 10, + }, + + # see RFC 4880 section 9.3 + compression => { uncompressed => 0, + zip => 1, + zlib => 2, + bzip2 => 3, + }, + + # see RFC 4880 section 9.4 + digests => { md5 => 1, + sha1 => 2, + ripemd160 => 3, + sha256 => 8, + sha384 => 9, + sha512 => 10, + sha224 => 11, + }, + + # see RFC 4880 section 5.2.3.21 + usage_flags => { certify => 0x01, + sign => 0x02, + encrypt_comms => 0x04, + encrypt_storage => 0x08, + encrypt => 0x0c, ## both comms and storage + split => 0x10, # the private key is split via secret sharing + authenticate => 0x20, + shared => 0x80, # more than one person holds the entire private key + }, + + # see RFC 4880 section 4.3 + packet_types => { pubkey_enc_session => 1, + sig => 2, + symkey_enc_session => 3, + onepass_sig => 4, + seckey => 5, + pubkey => 6, + sec_subkey => 7, + compressed_data => 8, + symenc_data => 9, + marker => 10, + literal => 11, + trust => 12, + uid => 13, + pub_subkey => 14, + uat => 17, + symenc_w_integrity => 18, + mdc => 19, + }, + + # see RFC 4880 section 5.2.1 + sig_types => { binary_doc => 0x00, + text_doc => 0x01, + standalone => 0x02, + generic_certification => 0x10, + persona_certification => 0x11, + casual_certification => 0x12, + positive_certification => 0x13, + subkey_binding => 0x18, + primary_key_binding => 0x19, + key_signature => 0x1f, + key_revocation => 0x20, + subkey_revocation => 0x28, + certification_revocation => 0x30, + timestamp => 0x40, + thirdparty => 0x50, + }, + + # see RFC 4880 section 5.2.3.23 + revocation_reasons => { no_reason_specified => 0, + key_superseded => 1, + key_compromised => 2, + key_retired => 3, + user_id_no_longer_valid => 32, + }, + + # see RFC 4880 section 5.2.3.1 + subpacket_types => { sig_creation_time => 2, + sig_expiration_time => 3, + exportable => 4, + trust_sig => 5, + regex => 6, + revocable => 7, + key_expiration_time => 9, + preferred_cipher => 11, + revocation_key => 12, + issuer => 16, + notation => 20, + preferred_digest => 21, + preferred_compression => 22, + keyserver_prefs => 23, + preferred_keyserver => 24, + primary_uid => 25, + policy_uri => 26, + usage_flags => 27, + signers_uid => 28, + revocation_reason => 29, + features => 30, + signature_target => 31, + embedded_signature => 32, + }, + + # bitstring (see RFC 4880 section 5.2.3.24) + features => { mdc => 0x01 + }, + + # bitstring (see RFC 4880 5.2.3.17) + keyserver_prefs => { nomodify => 0x80 + }, + }; + + +# takes a Math::BigInt, returns it formatted as OpenPGP MPI +# (RFC 4880 section 3.2) +sub mpi_pack { + my $num = shift; + + my $hex = $num->as_hex(); + $hex =~ s/^0x//; + # ensure we've got an even multiple of 2 nybbles here. + $hex = '0'.$hex + if (length($hex) % 2); + + my $val = pack('H*', $hex); + my $mpilen = length($val)*8; + +# this is a kludgy way to get the number of significant bits in the +# first byte: + my $bitsinfirstbyte = length(sprintf("%b", ord($val))); + + $mpilen -= (8 - $bitsinfirstbyte); + + return pack('n', $mpilen).$val; +} + +sub make_rsa_pub_key_body { + my $key = shift; + my $key_timestamp = shift; + + return + pack('CN', 4, $key_timestamp). + pack('C', $tables->{asym_algos}->{rsa}). + mpi_pack($key->{modulus}). + mpi_pack($key->{exponent}); +} + +sub fingerprint { + my $key = shift; + my $key_timestamp = shift; + + my $rsabody = make_rsa_pub_key_body($key, $key_timestamp); + + return Digest::SHA::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); +} + + +1; diff --git a/unit-tests/30.fingerprints/fpr.t b/unit-tests/30.fingerprints/fpr.t new file mode 100644 index 0000000..7218536 --- /dev/null +++ b/unit-tests/30.fingerprints/fpr.t @@ -0,0 +1,18 @@ +# -*- perl -*- +use Test::More; + +use Crypt::Monkeysphere::OpenPGP; +use Data::Dumper; + +use strict; + +my $timestamp = 1299825212; +my $key = { modulus => Math::BigInt->new('0xcceb95c3c00b8a12c9de4829a803302f76549a50ee9b7ee58ee3a75ed1839d77d2f57b766e9954581d64eb5599ae98326a028831fbadad8065d63bc5a7b8d831e06d363fd9954f271fda1d746674b0ad6e8dff9fc5ddd4608bdf95760372f50897637a379079f3eb2544099a4511fc8af8e5992e15df8eac619b58a9970a3bdb'), + exponent => Math::BigInt->new('0x10001'), + }; +plan tests =>1; + +is(unpack('H*', Crypt::Monkeysphere::OpenPGP::fingerprint($key, $timestamp)),"10cc971bbbb37b9152e8e759a2882699b47c6497"); + + + -- 2.26.2