X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=src%2Fshare%2Fkeytrans;h=ae4fb0993980afb1de16558a227775486ab89b5e;hb=d4d10f1f8ae42f9e9d81aabc5814aeeeb52aaa19;hp=171a1f64a910c0035ebea864fded44bbbf840b42;hpb=d0116abc28011849b0de688200b8782d24088021;p=monkeysphere.git diff --git a/src/share/keytrans b/src/share/keytrans index 171a1f6..ae4fb09 100755 --- a/src/share/keytrans +++ b/src/share/keytrans @@ -152,6 +152,14 @@ my $sig_types = { binary_doc => 0x00, }; +# see RFC 4880 section 5.2.3.23 +my $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 my $subpacket_types = { sig_creation_time => 2, sig_expiration_time => 3, @@ -421,7 +429,7 @@ sub fingerprint { # FIXME: handle DSA keys as well! -sub pem2openpgp { +sub makeselfsig { my $rsa = shift; my $uid = shift; my $args = shift; @@ -434,10 +442,7 @@ sub pem2openpgp { if (! defined $args->{sig_timestamp}) { $args->{sig_timestamp} = time(); } - if (! defined $args->{key_timestamp}) { - $args->{key_timestamp} = $args->{sig_timestamp} + 0; - } - my $key_timestamp = $args->{key_timestamp}; + my $key_timestamp = $args->{key_timestamp} + 0; # generate and aggregate subpackets: @@ -510,10 +515,7 @@ sub pem2openpgp { $feature_subpacket. $keyserver_pref; - return - make_packet($packet_types->{seckey}, make_rsa_sec_key_body($rsa, $key_timestamp)). - make_packet($packet_types->{uid}, $uid). - gensig($rsa, $uid, $args); + return gensig($rsa, $uid, $args); } # FIXME: handle non-RSA keys @@ -533,7 +535,7 @@ sub gensig { $rsa->use_pkcs1_padding(); if (! $rsa->check_key()) { - die "key does not check"; + die "key does not check\n"; } my $certtype = $args->{certification_type} + 0; @@ -557,7 +559,7 @@ sub gensig { my $key_timestamp = ($args->{key_timestamp} + 0); if ($key_timestamp > $sig_timestamp) { - die "key timestamp must not be later than signature timestamp"; + die "key timestamp must not be later than signature timestamp\n"; } my $creation_time_packet = pack('CCN', 5, $subpacket_types->{sig_creation_time}, $sig_timestamp); @@ -600,7 +602,6 @@ sub gensig { $sig_data_to_be_hashed. $trailer; - # FIXME: handle signatures over digests other than SHA256: my $data_hash = Digest::SHA::sha256_hex($datatosign); @@ -631,11 +632,11 @@ sub finduid { my $packetlen = shift; my $dummy; - ($tag == $packet_types->{uid}) or die "This should not be called on anything but a User ID packet"; + ($tag == $packet_types->{uid}) or die "This should not be called on anything but a User ID packet\n"; read($instr, $dummy, $packetlen); - $data->{uid} = {} unless defined $data->{uid}; $data->{uid}->{$dummy} = {}; + $data->{current}->{uid} = $dummy; } @@ -646,51 +647,30 @@ sub findsig { my $tag = shift; my $packetlen = shift; - ($tag == $packet_types->{sig}) or die "No calling findsig on anything other than a signature packet."; + ($tag == $packet_types->{sig}) or die "No calling findsig on anything other than a signature packet.\n"; my $dummy; my $readbytes = 0; - if ((undef $data->{key}) || - (undef $data->{uid}) || - (undef $data->{uid}->{$data->{target}->{uid}})) { - # this is not the user ID we are looking for. - read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; - } + read($instr, $dummy, $packetlen - $readbytes) or die "Could not read in this packet.\n"; - read($instr, $data, 6) or die "could not read signature header\n"; - my ($ver, $sigtype, $pubkeyalgo, $digestalgo, $subpacketsize) = unpack('CCCCn', $data); - if ($ver != 4) { - printf(STDERR "We only work with version 4 signatures."); - read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; - return; - } - if ($pubkeyalgo != $asym_algos->{rsa}) { - printf(STDERR "We can only work with RSA at the moment"); - read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; - return; - } - if ($sigtype != $sig_types->{positive_certification}) { - # FIXME: some weird implementations might have made generic, - # persona, or casual certifications instead of positive - # certifications for self-sigs. Probably should handle them too. - read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; + if ((! defined $data->{key}) || + (! defined $data->{uid}) || + (! defined $data->{uid}->{$data->{target}->{uid}})) { + # the user ID we are looking for has not been found yet. return; } - my $subpackets; - read($instr, $subpackets, $subpacketsize) or die "could not read hashed signature subpackets.\n"; - - read($instr, $subpacketsize, 2) or die "could not read unhashed signature subpacket size.\n"; - $subpacketsize = unpack('n', $subpacketsize); - - my $unhashedsubpackets; - read($instr, $unhashedsubpackets, $subpacketsize) or die "could not read unhashed signature subpackets.\n"; + # FIXME: if we get two primary keys on stdin, both with the same + # targetd user ID, we'll store signatures from both keys, which is + # probably wrong. - my $hashtail; - read($instr, $hashtail, 2) or die "could not read left 16 bits of digest.\n"; + # the current ID is not what we're looking for: + return if ($data->{current}->{uid} ne $data->{target}->{uid}); - # FIXME: RSA signatures should read in how many MPIs? + # just storing the raw signatures for the moment: + push @{$data->{sigs}}, make_packet($packet_types->{sig}, $dummy); + return; } @@ -817,7 +797,9 @@ sub openpgp2rsa { $fpr = uc($fpr); } - my $data = { 'fpr' => $fpr}; + my $data = { target => { fpr => $fpr, + }, + }; my $subs = { $packet_types->{pubkey} => \&findkey, $packet_types->{pub_subkey} => \&findkey, $packet_types->{seckey} => \&findkey, @@ -828,10 +810,60 @@ sub openpgp2rsa { return $data->{key}->{rsa}; } +sub adduserid { + my $instr = shift; + my $fpr = shift; + my $uid = shift; + my $args = shift; + + if ((! defined $fpr) || + (length($fpr) < 8)) { + die "We need at least 8 hex digits of fingerprint.\n"; + } + + $fpr = uc($fpr); + + if (! defined $uid) { + die "No User ID defined.\n"; + } + + my $data = { target => { fpr => $fpr, + uid => $uid, + }, + }; + my $subs = { $packet_types->{seckey} => \&findkey, + $packet_types->{uid} => \&finduid, + $packet_types->{sig} => \&findsig, + }; + + packetwalk($instr, $subs, $data); + + if ((! defined $data->{key}) || + (! defined $data->{key}->{rsa}) || + (! defined $data->{key}->{timestamp})) { + die "The key requested was not found.\n" + } + + if (defined $data->{uid}->{$uid}) { + die "The requested User ID '$uid' is already associated with this key.\n"; + } + $args->{key_timestamp} = $data->{key}->{timestamp}; + + return + make_packet($packet_types->{pubkey}, make_rsa_pub_key_body($data->{key}->{rsa}, $data->{key}->{timestamp})). + make_packet($packet_types->{uid}, $uid). + makeselfsig($data->{key}->{rsa}, + $uid, + $args); + +} + + sub revokeuserid { my $instr = shift; my $fpr = shift; my $uid = shift; + my $sigtime = shift; if ((! defined $fpr) || (length($fpr) < 8)) { @@ -845,10 +877,12 @@ sub revokeuserid { } my $data = { target => { fpr => $fpr, + uid => $uid, }, }; my $subs = { $packet_types->{seckey} => \&findkey, - $packet_types->{uid} => \&finduid + $packet_types->{uid} => \&finduid, + $packet_types->{sig} => \&findsig, }; packetwalk($instr, $subs, $data); @@ -864,9 +898,53 @@ sub revokeuserid { die "The key requested was not found." } + my $revocation_reason = 'No longer using this hostname'; + if (defined $data->{revocation_reason}) { + $revocation_reason = $data->{revocation_reason}; + } + + my $rev_reason_subpkt = prefixsubpacket(pack('CC', + $subpacket_types->{revocation_reason}, + $revocation_reasons->{user_id_no_longer_valid}). + $revocation_reason); + + if (! defined $sigtime) { + $sigtime = time(); + } # what does a signature like this look like? + my $args = { key_timestamp => $data->{key}->{timestamp}, + sig_timestamp => $sigtime, + certification_type => $sig_types->{certification_revocation}, + hashed_subpackets => $rev_reason_subpkt, + }; - return 'abc'; + return + make_packet($packet_types->{pubkey}, make_rsa_pub_key_body($data->{key}->{rsa}, $data->{key}->{timestamp})). + make_packet($packet_types->{uid}, $uid). + join('', @{$data->{sigs}}). + gensig($data->{key}->{rsa}, $uid, $args); +} + + +# see 5.2.3.1 for tips on how to calculate the length of a subpacket: +sub prefixsubpacket { + my $subpacket = shift; + + my $len = length($subpacket); + my $prefix; + use bytes; + if ($len < 192) { + # one byte: + $prefix = pack('C', $len); + } elsif ($len < 16576) { + my $in = $len - 192; + my $second = $in%256; + my $first = ($in - $second)>>8; + $prefix = pack('CC', $first + 192, $second) + } else { + $prefix = pack('CN', 255, $len); + } + return $prefix.$subpacket; } @@ -963,10 +1041,18 @@ for (basename($0)) { $rsa = Crypt::OpenSSL::RSA->new_private_key($stdin); } - print pem2openpgp($rsa, + my $key_timestamp = $ENV{PEM2OPENPGP_KEY_TIMESTAMP}; + my $sig_timestamp = $ENV{PEM2OPENPGP_TIMESTAMP}; + $sig_timestamp = time() if (!defined $sig_timestamp); + $key_timestamp = $sig_timestamp if (!defined $key_timestamp); + + print + make_packet($packet_types->{seckey}, make_rsa_sec_key_body($rsa, $key_timestamp)). + make_packet($packet_types->{uid}, $uid). + makeselfsig($rsa, $uid, - { sig_timestamp => $ENV{PEM2OPENPGP_TIMESTAMP}, - key_timestamp => $ENV{PEM2OPENPGP_KEY_TIMESTAMP}, + { sig_timestamp => $sig_timestamp, + key_timestamp => $key_timestamp, expiration => $ENV{PEM2OPENPGP_EXPIRATION}, usage_flags => $ENV{PEM2OPENPGP_USAGE_FLAGS}, } @@ -988,16 +1074,38 @@ for (basename($0)) { die "No matching key found.\n"; } } - elsif (/^revokeuserid$/) { - my $fpr = shift; - my $uid = shift; - my $instream; - open($instream,'-'); - binmode($instream, ":bytes"); - - my $revcert = revokeuserid($instream, $fpr, $uid); - - print $revcert; + elsif (/^keytrans$/) { + # subcommands when keytrans is invoked directly are UNSUPPORTED, + # UNDOCUMENTED, and WILL NOT BE MAINTAINED. + my $subcommand = shift; + for ($subcommand) { + if (/^revokeuserid$/) { + my $fpr = shift; + my $uid = shift; + my $instream; + open($instream,'-'); + binmode($instream, ":bytes"); + + my $revcert = revokeuserid($instream, $fpr, $uid, $ENV{PEM2OPENPGP_TIMESTAMP}); + + print $revcert; + } elsif (/^adduserid$/) { + my $fpr = shift; + my $uid = shift; + my $instream; + open($instream,'-'); + binmode($instream, ":bytes"); + my $newuid = adduserid($instream, $fpr, $uid, + { sig_timestamp => $ENV{PEM2OPENPGP_TIMESTAMP}, + expiration => $ENV{PEM2OPENPGP_EXPIRATION}, + usage_flags => $ENV{PEM2OPENPGP_USAGE_FLAGS}, + }); + + print $newuid; + } else { + die "Unrecognized subcommand. keytrans subcommands are not a stable interface!\n"; + } + } } else { die "Unrecognized keytrans call.\n";