X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;ds=sidebyside;f=src%2Fkeytrans%2Fpem2openpgp;h=382e14fdb3c6b0287879b136fc1db7ddfb12161e;hb=4a7350c9ae0b789210583af169071c43d2c43ab4;hp=1575671eab27be5b2bf6075679dbe5e39c430ba4;hpb=099e48efe48e6d7f5bbc5ad61b5ed88c468623d2;p=monkeysphere.git diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 1575671..382e14f 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -4,6 +4,10 @@ # User ID as the first argument, and generate an OpenPGP certificate # from it. +# Usage: + +# pem2openpgp 'ssh://'$(hostname -f) < /etc/ssh/ssh_host_rsa_key | gpg --import + # Authors: # Jameson Rollins # Daniel Kahn Gillmor @@ -23,13 +27,17 @@ use MIME::Base64; ## make sure all length() and substr() calls use bytes only: use bytes; +my $uid = shift; + +# FIXME: fail if there is no given user ID; or should we default to +# hostname_long() from Sys::Hostname::Long ? + # make an old-style packet out of the given packet type and body. # old-style (see RFC 4880 section 4.2) sub make_packet { my $type = shift; my $body = shift; -# FIXME: yet another length(): my $len = length($body); my $lenbytes; @@ -62,19 +70,20 @@ sub make_packet { sub mpi_pack { my $num = shift; - my $hex = $num->to_hex(); - - my $mpilen = length($hex)*4; + my $val = $num->to_bin(); + my $mpilen = length($val)*8; -# this is a kludgy way to get the number of bits in the first byte: - my $bitsinfirstbyte = length(sprintf("%b", hex(substr $hex, 0, 2))); +# 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).$num->to_bin(); + return pack('n', $mpilen).$val; } + # FIXME: genericize this to accept either RSA or DSA keys: -sub make_rsa_key_body { +sub make_rsa_pub_key_body { my $key = shift; my $timestamp = shift; @@ -93,12 +102,126 @@ sub fingerprint { my $key = shift; my $timestamp = shift; - my $rsabody = make_rsa_key_body($key, $timestamp); + my $rsabody = make_rsa_pub_key_body($key, $timestamp); - return Digest::SHA1::sha1_hex(pack('Cn', 0x99, length($rsabody)).$rsabody); + return Digest::SHA1::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); } -my $holdTerminator = $/; +# FIXME: replace the opaque numbers below with +# semantically-meaningful references based on these tables. + +# see RFC 4880 section 9.1 (ignoring deprecated algorithms for now) +my $asym_algos = { rsa => 1, + elgamal => 16, + dsa => 17, + }; + +# see RFC 4880 section 9.2 +my $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 +my $zips = { uncompressed => 0, + zip => 1, + zlib => 2, + bzip2 => 3, + }; + +# see RFC 4880 section 9.4 +my $digests = { md5 => 1, + sha1 => 2, + ripemd160 => 3, + sha256 => 8, + sha384 => 9, + sha512 => 10, + sha224 => 11, + }; + +# see RFC 4880 section 5.2.3.21 +my $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 +my $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 +my $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.1 +my $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, + 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, + }; + +# we're just not dealing with newline business right now. slurp in +# the whole file. undef $/; my $buf = ; @@ -106,16 +229,15 @@ my $buf = ; my $rsa = Crypt::OpenSSL::RSA->new_private_key($buf); $rsa->use_sha1_hash(); -$rsa->use_no_padding(); + +# see page 22 of RFC 4880 for why i think this is the right padding +# choice to use: +$rsa->use_pkcs1_padding(); if (! $rsa->check_key()) { die "key does not check"; } -my $uid = 'fake key (do not use) '; - - - my $version = pack('C', 4); # strong assertion of identity: my $sigtype = pack('C', 0x13); @@ -124,19 +246,26 @@ my $pubkey_algo = pack('C', 1); # SHA1 my $hash_algo = pack('C', 2); +# FIXME: i'm worried about generating a bazillion new OpenPGP +# certificates from the same key, which could easily happen if you run +# this script more than once against the same key. How can we prevent +# this? - -my $timestamp = 1231003584; +# could an environment variable (if set) override the current time? +my $timestamp = time(); my $creation_time_packet = pack('CCN', 5, 2, $timestamp); -# usage: signing and certification: -my $flags = 0x03; +# FIXME: HARDCODED: what if someone wants to select a different set of +# usage flags? For now, we do only authentication. +my $flags = $usage_flags->{authenticate}; my $usage_packet = pack('CCC', 2, 27, $flags); -# expire in 2 days: +# FIXME: HARDCODED: how should we determine how far off to set the +# expiration date? default is to expire in 2 days, which is insanely +# short (but good for testing). my $expires_in = 86400*2; my $expiration_packet = pack('CCN', 5, 9, $expires_in); @@ -166,7 +295,6 @@ my $subpackets_to_be_hashed = $features. $keyserver_pref; -#FIXME: what's the right way to get length()? my $subpacket_octets = pack('n', length($subpackets_to_be_hashed)); my $sig_data_to_be_hashed = @@ -177,13 +305,13 @@ my $sig_data_to_be_hashed = $subpacket_octets. $subpackets_to_be_hashed; -my $pubkey = make_rsa_key_body($rsa, $timestamp); +my $pubkey = make_rsa_pub_key_body($rsa, $timestamp); #open(KEYFILE, "new_from_bin($rsa->sign($datatosign)); my $sig_body = $sig_data_to_be_hashed. -# FIXME: another dubious length() call. pack('n', length($issuer_packet)). $issuer_packet. pack('n', hex(substr($data_hash, 0, 4))). mpi_pack($sig); -print make_packet(6, $pubkey); -print make_packet(13, $uid); -print make_packet(2, $sig_body); +print + make_packet(6, $pubkey). + make_packet(13, $uid). + make_packet(2, $sig_body); + -$/ = $holdTerminator;