From ef9469ee700eacfb9da0b2d897b82fbe1287e864 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 02:17:03 -0500 Subject: added first pass at perl script to convert existing PEM-encoded RSA keys into OpenPGP keys --- src/keytrans/pem2openpgp | 180 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100755 src/keytrans/pem2openpgp (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp new file mode 100755 index 0000000..59f9bb0 --- /dev/null +++ b/src/keytrans/pem2openpgp @@ -0,0 +1,180 @@ +#!/usr/bin/perl -w -T + +# pem2openpgp: take a PEM-encoded RSA private-key on standard input, a +# User ID as the first argument, and generate an OpenPGP certificate +# from it. + +# Authors: +# Jameson Rollins +# Daniel Kahn Gillmor + +# Started on: 2009-01-07 02:01:19-0500 + +# License: GPL v3 or later (we may need to adjust this given that this +# connects to OpenSSL via perl) + +use strict; +use warnings; +use Crypt::OpenSSL::RSA; +use Crypt::OpenSSL::Bignum; +use Digest::SHA1; +use MIME::Base64; + +my $holdTerminator = $/; +undef $/; +my $buf = ; + + +my $rsa = Crypt::OpenSSL::RSA->new_private_key($buf); + +$rsa->use_sha1_hash(); +$rsa->use_no_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); +# RSA +my $pubkey_algo = pack('C', 1); +# SHA1 +my $hash_algo = pack('C', 2); + + + +my $timestamp = 1231003584; + +my $creation_time_packet = pack('CCN', 5, 2, $timestamp); + + +# usage: signing and certification: +my $flags = 0x03; +my $usage_packet = pack('CCC', 2, 27, $flags); + + +# expire in 2 days: +my $expires_in = 86400*2; +my $expiration_packet = pack('CCN', 5, 9, $expires_in); + + +# prefer AES-256, AES-192, AES-128, CAST5, 3DES: +my $pref_sym_algos = pack('CCCCCCC', 6, 11, 9, 8, 7, 3, 2); + +# prefer SHA-1, SHA-256, RIPE-MD/160 +my $pref_hash_algos = pack('CCCCC', 4, 21, 2, 8, 3); + +# prefer ZLIB, BZip2, ZIP +my $pref_zip_algos = pack('CCCCC', 4, 22, 2, 3, 1); + +# we support the MDC feature: +my $features = pack('CCC', 2, 30, 1); + +# keyserver preference: only owner modify (???): +my $keyserver_pref = pack('CCC', 2, 23, 0x80); + +my $subpackets_to_be_hashed = + $creation_time_packet. + $usage_packet. + $expiration_packet. + $pref_sym_algos. + $pref_hash_algos. + $pref_zip_algos. + $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 = + $version. + $sigtype. + $pubkey_algo. + $hash_algo. + $subpacket_octets. + $subpackets_to_be_hashed; + + +my ($n, $e, $d, $p, $q) = $rsa->get_key_parameters(); + + +open(KEYFILE, "; + +# FIXME: $keyid should be generated from the public key instead of +# hardcoded: +my $keyid = '5616d7cb02e69446'; + +# the v4 signature trailer is: + +# version number, literal 0xff, and then a 4-byte count of the +# signature data itself. +my $trailer = pack('CCN', 4, 0xff, length($sig_data_to_be_hashed)); + +# FIXME: length() is probably not right here either in the event that +# the uid uses unicode. +my $uid_data = + pack('CN', 0xb4, length($uid)). + $uid; + +my $datatosign = + $key_data. + $uid_data. + $sig_data_to_be_hashed. + $trailer; + +my $data_hash = Digest::SHA1::sha1_hex($datatosign); + + +my $issuer_packet = pack('CCH16', 9, 16, $keyid); + +my $sig = $rsa->sign($datatosign); + +my $bigsig = Crypt::OpenSSL::Bignum->new_from_bin($sig); + + +my $hex = $bigsig->to_hex(); + +my $mpilen = length($hex)*4; + +# 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))); + +$mpilen -= (8 - $bitsinfirstbyte); + +# emit two octets representing $mpilen, followed by the signature itself: + + +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))). + pack("n" , $mpilen). + $sig; + +# FIXME: yet another length(): +my $len = length($sig_body); + +my $header; + +if ($len < 2**8) { + $header = pack('CC', 0x88, $len); +} elsif ($len < 2**16) { + $header = pack('Cn', 0x89, $len); +} elsif ($len < 2**31) { + $header = pack('CN', 0x8a, $len); +} else { + # what the hell do we do here? + $header = pack('C', 0x8b); +} + +print $header.$sig_body; + +$/ = $holdTerminator; -- cgit v1.2.3 From c2da43d48e1d8c54c089081d7f316bb925f426d9 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 12:31:37 -0500 Subject: clean up a bit of pem2openpgp and remove some of the hardcoded data. --- src/keytrans/pem2openpgp | 102 ++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 37 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 59f9bb0..38baa95 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -20,6 +20,58 @@ use Crypt::OpenSSL::Bignum; use Digest::SHA1; use MIME::Base64; + +# 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; + my $lencode; + + if ($len < 2**8) { + $lenbytes = 0; + $lencode = 'C'; + } elsif ($len < 2**16) { + $lenbytes = 1; + $lencode = 'n'; + } elsif ($len < 2**31) { + ## not testing against full 32 bits because i don't want to deal + ## with potential overflow. + $lenbytes = 2; + $lencode = 'N'; + } else { + ## what the hell do we do here? + $lenbytes = 3; + $lencode = ''; + } + + return pack('C'.$lencode, 0x80 + ($type * 4) + $lenbytes, $len). + $body; +} + + +# takes a Crypt::OpenSSL::Bignum +sub mpi_pack { + my $num = shift; + + my $hex = $num->to_hex(); + + my $mpilen = length($hex)*4; + +# 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))); + + $mpilen -= (8 - $bitsinfirstbyte); + + return pack('n', $mpilen).$num->to_bin(); +} + + my $holdTerminator = $/; undef $/; my $buf = ; @@ -103,8 +155,14 @@ my $sig_data_to_be_hashed = my ($n, $e, $d, $p, $q) = $rsa->get_key_parameters(); -open(KEYFILE, "; +my $pubkey = + pack('CN', 4, $timestamp). + $pubkey_algo. + mpi_pack($n). + mpi_pack($e); + +#open(KEYFILE, "sign($datatosign); - -my $bigsig = Crypt::OpenSSL::Bignum->new_from_bin($sig); - - -my $hex = $bigsig->to_hex(); - -my $mpilen = length($hex)*4; - -# 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))); - -$mpilen -= (8 - $bitsinfirstbyte); - -# emit two octets representing $mpilen, followed by the signature itself: - +my $sig = Crypt::OpenSSL::Bignum->new_from_bin($rsa->sign($datatosign)); my $sig_body = $sig_data_to_be_hashed. @@ -156,25 +199,10 @@ my $sig_body = pack('n', length($issuer_packet)). $issuer_packet. pack('n', hex(substr($data_hash, 0, 4))). - pack("n" , $mpilen). - $sig; - -# FIXME: yet another length(): -my $len = length($sig_body); - -my $header; - -if ($len < 2**8) { - $header = pack('CC', 0x88, $len); -} elsif ($len < 2**16) { - $header = pack('Cn', 0x89, $len); -} elsif ($len < 2**31) { - $header = pack('CN', 0x8a, $len); -} else { - # what the hell do we do here? - $header = pack('C', 0x8b); -} + mpi_pack($sig); -print $header.$sig_body; +print make_packet(6, $pubkey); +print make_packet(13, $uid); +print make_packet(2, $sig_body); $/ = $holdTerminator; -- cgit v1.2.3 From 099e48efe48e6d7f5bbc5ad61b5ed88c468623d2 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 13:27:32 -0500 Subject: removed last hardcoded data in pem2openpgp; it seems to work with our test key. --- src/keytrans/pem2openpgp | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 38baa95..1575671 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -20,6 +20,8 @@ use Crypt::OpenSSL::Bignum; use Digest::SHA1; use MIME::Base64; +## make sure all length() and substr() calls use bytes only: +use bytes; # make an old-style packet out of the given packet type and body. # old-style (see RFC 4880 section 4.2) @@ -55,7 +57,8 @@ sub make_packet { } -# takes a Crypt::OpenSSL::Bignum +# takes a Crypt::OpenSSL::Bignum, returns it formatted as OpenPGP MPI +# (RFC 4880 section 3.2) sub mpi_pack { my $num = shift; @@ -70,7 +73,30 @@ sub mpi_pack { return pack('n', $mpilen).$num->to_bin(); } +# FIXME: genericize this to accept either RSA or DSA keys: +sub make_rsa_key_body { + my $key = shift; + my $timestamp = shift; + my ($n, $e) = $key->get_key_parameters(); + + return + pack('CN', 4, $timestamp). + pack('C', 1). # RSA + mpi_pack($n). + mpi_pack($e); + +} + +# expects an RSA key (public or private) and a timestamp +sub fingerprint { + my $key = shift; + my $timestamp = shift; + + my $rsabody = make_rsa_key_body($key, $timestamp); + + return Digest::SHA1::sha1_hex(pack('Cn', 0x99, length($rsabody)).$rsabody); +} my $holdTerminator = $/; undef $/; @@ -130,7 +156,7 @@ my $features = pack('CCC', 2, 30, 1); # keyserver preference: only owner modify (???): my $keyserver_pref = pack('CCC', 2, 23, 0x80); -my $subpackets_to_be_hashed = +my $subpackets_to_be_hashed = $creation_time_packet. $usage_packet. $expiration_packet. @@ -151,22 +177,13 @@ my $sig_data_to_be_hashed = $subpacket_octets. $subpackets_to_be_hashed; - -my ($n, $e, $d, $p, $q) = $rsa->get_key_parameters(); - - -my $pubkey = - pack('CN', 4, $timestamp). - $pubkey_algo. - mpi_pack($n). - mpi_pack($e); +my $pubkey = make_rsa_key_body($rsa, $timestamp); #open(KEYFILE, " Date: Wed, 7 Jan 2009 13:35:17 -0500 Subject: use bytes in pem2openpgp to ensure that length calculations are done by octet and not by character. --- src/keytrans/pem2openpgp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 1575671..f6e2d4f 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -29,7 +29,6 @@ sub make_packet { my $type = shift; my $body = shift; -# FIXME: yet another length(): my $len = length($body); my $lenbytes; @@ -62,17 +61,18 @@ sub make_packet { sub mpi_pack { my $num = shift; - my $hex = $num->to_hex(); + my $val = $num->to_bin(); + my $mpilen = length($val)*8; - my $mpilen = length($hex)*4; - -# 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 { my $key = shift; @@ -166,7 +166,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 = @@ -191,8 +190,6 @@ my $keyid = substr(fingerprint($rsa, $timestamp), 40 - 16, 16); # signature data itself. my $trailer = pack('CCN', 4, 0xff, length($sig_data_to_be_hashed)); -# FIXME: length() is probably not right here either in the event that -# the uid uses unicode. my $uid_data = pack('CN', 0xb4, length($uid)). $uid; @@ -212,7 +209,6 @@ my $sig = Crypt::OpenSSL::Bignum->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))). -- cgit v1.2.3 From ad8c2c433a163b9b29281b80fb1390bfcd9756bd Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 14:59:40 -0500 Subject: pem2openpgp now accepts a choice of User ID on stdin. --- src/keytrans/pem2openpgp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index f6e2d4f..3fdc469 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -23,6 +23,10 @@ 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. + # make an old-style packet out of the given packet type and body. # old-style (see RFC 4880 section 4.2) sub make_packet { @@ -112,10 +116,6 @@ 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); -- cgit v1.2.3 From c71c0212bc36ed18d6df60c7a1dc0c3f6c541339 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 15:02:05 -0500 Subject: clarifying make_rsa_key_body() to make_rsa_pub_key_body() --- src/keytrans/pem2openpgp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 3fdc469..c5277cd 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -78,7 +78,7 @@ sub mpi_pack { } # 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; @@ -97,7 +97,7 @@ 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); } @@ -176,7 +176,7 @@ 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, " Date: Wed, 7 Jan 2009 15:46:19 -0500 Subject: pem2openpgp: clean up comments, treat fingerprint as raw data instead of ascii --- src/keytrans/pem2openpgp | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index c5277cd..7522c8f 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 @@ -25,7 +29,8 @@ use bytes; my $uid = shift; -# FIXME: fail if there is no given user ID. +# 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) @@ -99,10 +104,11 @@ sub fingerprint { 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 = $/; +# we're just not dealing with newline business right now. slurp in +# the whole file. undef $/; my $buf = ; @@ -124,9 +130,13 @@ 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); @@ -136,7 +146,9 @@ my $flags = 0x03; 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); @@ -181,8 +193,8 @@ my $pubkey = make_rsa_pub_key_body($rsa, $timestamp); #open(KEYFILE, "new_from_bin($rsa->sign($datatosign)); @@ -214,8 +226,9 @@ my $sig_body = 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; -- cgit v1.2.3 From f8344402aebe5f0497a81934b980b9ed6ea7a6a2 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 16:17:49 -0500 Subject: pem2openpgp: break out usage flags, default to creating an authentication-capable primary key. --- src/keytrans/pem2openpgp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 7522c8f..2fa221d 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -107,6 +107,23 @@ sub fingerprint { return Digest::SHA1::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); } +# FIXME: make tables of relevant identifiers: digest algorithms, +# ciphers, asymmetric crypto, packet types, subpacket types, signature +# types. As these are created, replace the opaque numbers below with +# semantically-meaningful code. + +# 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 + }; + + # we're just not dealing with newline business right now. slurp in # the whole file. undef $/; @@ -141,8 +158,9 @@ 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); -- cgit v1.2.3 From efb99a4677ec05fb481e50bbb739f066c4025d25 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 7 Jan 2009 19:24:05 -0500 Subject: pem2openpgp: make lookup tables of relevant parameters. --- src/keytrans/pem2openpgp | 105 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 4 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 2fa221d..e76ba6f 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -107,10 +107,43 @@ sub fingerprint { return Digest::SHA1::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); } -# FIXME: make tables of relevant identifiers: digest algorithms, -# ciphers, asymmetric crypto, packet types, subpacket types, signature -# types. As these are created, replace the opaque numbers below with -# semantically-meaningful code. +# 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, + 3des => 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, @@ -124,6 +157,70 @@ my $usage_flags = { certify => 0x01, }; +# 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 $/; -- cgit v1.2.3 From 4a7350c9ae0b789210583af169071c43d2c43ab4 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 8 Jan 2009 13:36:49 -0500 Subject: fix stupid typos; switch padding during rsa signatures to that specified in RFC 4880 --- src/keytrans/pem2openpgp | 56 +++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index e76ba6f..382e14f 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -114,17 +114,17 @@ sub fingerprint { my $asym_algos = { rsa => 1, elgamal => 16, dsa => 17, - } + }; # see RFC 4880 section 9.2 my $ciphers = { plaintext => 0, idea => 1, - 3des => 2, + tripledes => 2, cast5 => 3, blowfish => 4, aes128 => 7, aes192 => 8, - aes256 => 9 + aes256 => 9, twofish => 10, }; @@ -156,7 +156,6 @@ my $usage_flags = { certify => 0x01, 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, @@ -197,28 +196,28 @@ my $sig_types = { binary_doc => 0x00, # 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, +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 @@ -230,7 +229,10 @@ 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"; -- cgit v1.2.3 From 3f5960cf4ba2f938c677c27e3296e6feae2f56aa Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sat, 10 Jan 2009 18:42:57 -0500 Subject: pem2openpgp: replace raw numbers with semantic labelling to make it more readable. --- src/keytrans/pem2openpgp | 61 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 19 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 382e14f..637eba2 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -208,6 +208,7 @@ my $subpacket_types = { sig_creation_time => 2, issuer => 16, notation => 20, preferred_digest => 21, + preferred_compression => 22, keyserver_prefs => 23, preferred_keyserver => 24, primary_uid => 25, @@ -220,6 +221,14 @@ my $subpacket_types = { sig_creation_time => 2, embedded_signature => 32, }; +# bitstring (see RFC 4880 section 5.2.3.24) +my $features = { mdc => 0x01 + }; + +# bitstring (see RFC 4880 5.2.3.17) +my $keyserver_prefs = { nomodify => 0x80 + }; + # we're just not dealing with newline business right now. slurp in # the whole file. undef $/; @@ -240,11 +249,11 @@ if (! $rsa->check_key()) { my $version = pack('C', 4); # strong assertion of identity: -my $sigtype = pack('C', 0x13); +my $sigtype = pack('C', $sig_types->{positive_certification}); # RSA -my $pubkey_algo = pack('C', 1); +my $pubkey_algo = pack('C', $asym_algos->{rsa}); # SHA1 -my $hash_algo = pack('C', 2); +my $hash_algo = pack('C', $digests->{sha1}); # FIXME: i'm worried about generating a bazillion new OpenPGP # certificates from the same key, which could easily happen if you run @@ -254,36 +263,51 @@ my $hash_algo = pack('C', 2); # could an environment variable (if set) override the current time? my $timestamp = time(); -my $creation_time_packet = pack('CCN', 5, 2, $timestamp); +my $creation_time_packet = pack('CCN', 5, $subpacket_types->{sig_creation_time}, $timestamp); # 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); +my $usage_packet = pack('CCC', 2, $subpacket_types->{usage_flags}, $usage_flags->{authenticate}); # 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); +my $expiration_packet = pack('CCN', 5, $subpacket_types->{key_expiration_time}, $expires_in); # prefer AES-256, AES-192, AES-128, CAST5, 3DES: -my $pref_sym_algos = pack('CCCCCCC', 6, 11, 9, 8, 7, 3, 2); +my $pref_sym_algos = pack('CCCCCCC', 6, $subpacket_types->{preferred_cipher}, + $ciphers->{aes256}, + $ciphers->{aes192}, + $ciphers->{aes128}, + $ciphers->{cast5}, + $ciphers->{tripledes} + ); # prefer SHA-1, SHA-256, RIPE-MD/160 -my $pref_hash_algos = pack('CCCCC', 4, 21, 2, 8, 3); +my $pref_hash_algos = pack('CCCCC', 4, $subpacket_types->{preferred_digest}, + $digests->{sha1}, + $digests->{sha256}, + $digests->{ripemd160} + ); # prefer ZLIB, BZip2, ZIP -my $pref_zip_algos = pack('CCCCC', 4, 22, 2, 3, 1); +my $pref_zip_algos = pack('CCCCC', 4, $subpacket_types->{preferred_compression}, + $zips->{zlib}, + $zips->{bzip2}, + $zips->{zip} + ); # we support the MDC feature: -my $features = pack('CCC', 2, 30, 1); +my $feature_subpacket = pack('CCC', 2, $subpacket_types->{features}, + $features->{mdc}); # keyserver preference: only owner modify (???): -my $keyserver_pref = pack('CCC', 2, 23, 0x80); +my $keyserver_pref = pack('CCC', 2, $subpacket_types->{keyserver_prefs}, + $keyserver_prefs->{nomodify}); my $subpackets_to_be_hashed = $creation_time_packet. @@ -292,7 +316,7 @@ my $subpackets_to_be_hashed = $pref_sym_algos. $pref_hash_algos. $pref_zip_algos. - $features. + $feature_subpacket. $keyserver_pref; my $subpacket_octets = pack('n', length($subpackets_to_be_hashed)); @@ -307,8 +331,7 @@ my $sig_data_to_be_hashed = my $pubkey = make_rsa_pub_key_body($rsa, $timestamp); -#open(KEYFILE, "{pubkey}, $pubkey); # take the last 8 bytes of the fingerprint as the keyid: my $keyid = substr(fingerprint($rsa, $timestamp), 20 - 8, 8); @@ -332,7 +355,7 @@ my $datatosign = my $data_hash = Digest::SHA1::sha1_hex($datatosign); -my $issuer_packet = pack('CCa8', 9, 16, $keyid); +my $issuer_packet = pack('CCa8', 9, $subpacket_types->{issuer}, $keyid); my $sig = Crypt::OpenSSL::Bignum->new_from_bin($rsa->sign($datatosign)); @@ -344,8 +367,8 @@ my $sig_body = mpi_pack($sig); print - make_packet(6, $pubkey). - make_packet(13, $uid). - make_packet(2, $sig_body); + make_packet($packet_types->{pubkey}, $pubkey). + make_packet($packet_types->{uid}, $uid). + make_packet($packet_types->{sig}, $sig_body); -- cgit v1.2.3 From 4af5666101d302692f76671c08188141289f13f3 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 11 Jan 2009 20:10:34 -0500 Subject: pem2openpgp: reorganized some code, put in initial function to try to create secret keys. we seem to be a bit of modular arithmetic away from creating private keys in an OpenPGP-style format. --- src/keytrans/pem2openpgp | 191 ++++++++++++++++++++++++++++------------------- 1 file changed, 115 insertions(+), 76 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 637eba2..fa92297 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -32,83 +32,7 @@ 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; - - my $len = length($body); - - my $lenbytes; - my $lencode; - - if ($len < 2**8) { - $lenbytes = 0; - $lencode = 'C'; - } elsif ($len < 2**16) { - $lenbytes = 1; - $lencode = 'n'; - } elsif ($len < 2**31) { - ## not testing against full 32 bits because i don't want to deal - ## with potential overflow. - $lenbytes = 2; - $lencode = 'N'; - } else { - ## what the hell do we do here? - $lenbytes = 3; - $lencode = ''; - } - - return pack('C'.$lencode, 0x80 + ($type * 4) + $lenbytes, $len). - $body; -} - - -# takes a Crypt::OpenSSL::Bignum, returns it formatted as OpenPGP MPI -# (RFC 4880 section 3.2) -sub mpi_pack { - my $num = shift; - - my $val = $num->to_bin(); - 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; -} - -# FIXME: genericize this to accept either RSA or DSA keys: -sub make_rsa_pub_key_body { - my $key = shift; - my $timestamp = shift; - - my ($n, $e) = $key->get_key_parameters(); - - return - pack('CN', 4, $timestamp). - pack('C', 1). # RSA - mpi_pack($n). - mpi_pack($e); - -} - -# expects an RSA key (public or private) and a timestamp -sub fingerprint { - my $key = shift; - my $timestamp = shift; - - my $rsabody = make_rsa_pub_key_body($key, $timestamp); - - return Digest::SHA1::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); -} - -# 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, @@ -229,6 +153,120 @@ my $features = { mdc => 0x01 my $keyserver_prefs = { nomodify => 0x80 }; +###### end lookup tables ###### + +# FIXME: if we want to be able to interpret openpgp data as well as +# produce it, we need to produce key/value-swapped lookup tables as well. + + +# 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; + + my $len = length($body); + + my $lenbytes; + my $lencode; + + if ($len < 2**8) { + $lenbytes = 0; + $lencode = 'C'; + } elsif ($len < 2**16) { + $lenbytes = 1; + $lencode = 'n'; + } elsif ($len < 2**31) { + ## not testing against full 32 bits because i don't want to deal + ## with potential overflow. + $lenbytes = 2; + $lencode = 'N'; + } else { + ## what the hell do we do here? + $lenbytes = 3; + $lencode = ''; + } + + return pack('C'.$lencode, 0x80 + ($type * 4) + $lenbytes, $len). + $body; +} + + +# takes a Crypt::OpenSSL::Bignum, returns it formatted as OpenPGP MPI +# (RFC 4880 section 3.2) +sub mpi_pack { + my $num = shift; + + my $val = $num->to_bin(); + 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; +} + +# see the bottom of page 43 of RFC 4880 +sub simple_checksum { + my $bytes = shift; + + return unpack("%C*",$bytes) % 65536; +} + +# FIXME: genericize these to accept either RSA or DSA keys: +sub make_rsa_pub_key_body { + my $key = shift; + my $timestamp = shift; + + my ($n, $e) = $key->get_key_parameters(); + + return + pack('CN', 4, $timestamp). + pack('C', $asym_algos->{rsa}). + mpi_pack($n). + mpi_pack($e); +} +sub make_rsa_sec_key_body { + my $key = shift; + my $timestamp = shift; + + # we're not using $a and $b, but we need them to get to $c. + my ($n, $e, $d, $p, $q, $a, $b, $c) = $key->get_key_parameters(); + + my $secret_material = mpi_pack($d). + mpi_pack($p). + mpi_pack($q). + mpi_pack($c); + + # FIXME: according to Crypt::OpenSSL::RSA, $c is 1/q mod p; but + # according to sec 5.5.3 of RFC 4880, this last argument should + # instead be: u, the multiplicative inverse of p, mod q. i don't + # see a simple way to generate this number from the perl module + # directly yet. + + return + pack('CN', 4, $timestamp). + pack('C', $asym_algos->{rsa}). + mpi_pack($n). + mpi_pack($e). + pack('C', 0). # seckey material is not encrypted -- see RFC 4880 sec 5.5.3 + $secret_material. + simple_checksum($secret_material); +} + +# expects an RSA key (public or private) and a timestamp +sub fingerprint { + my $key = shift; + my $timestamp = shift; + + my $rsabody = make_rsa_pub_key_body($key, $timestamp); + + return Digest::SHA1::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); +} + # we're just not dealing with newline business right now. slurp in # the whole file. undef $/; @@ -330,6 +368,7 @@ my $sig_data_to_be_hashed = $subpackets_to_be_hashed; my $pubkey = make_rsa_pub_key_body($rsa, $timestamp); +my $seckey = make_rsa_sec_key_body($rsa, $timestamp); my $key_data = make_packet($packet_types->{pubkey}, $pubkey); -- cgit v1.2.3 From ae9a949163f6850c7ef6a260d6d7b086a622d787 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 11 Jan 2009 23:01:10 -0500 Subject: pem2openpgp: implemented extended euclidean algorithm to find modular multiplicative inverse. this lets us compute the value we need for secret key material. --- src/keytrans/pem2openpgp | 49 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index fa92297..c74ca25 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -21,6 +21,7 @@ use strict; use warnings; use Crypt::OpenSSL::RSA; use Crypt::OpenSSL::Bignum; +use Crypt::OpenSSL::Bignum::CTX; use Digest::SHA1; use MIME::Base64; @@ -213,7 +214,7 @@ sub mpi_pack { sub simple_checksum { my $bytes = shift; - return unpack("%C*",$bytes) % 65536; + return unpack("%32W*",$bytes) % 65536; } # FIXME: genericize these to accept either RSA or DSA keys: @@ -229,6 +230,46 @@ sub make_rsa_pub_key_body { mpi_pack($n). mpi_pack($e); } + +# calculate the multiplicative inverse of a mod b this is euclid's +# extended algorithm. For more information see: +# http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm the +# arguments here should be Crypt::OpenSSL::Bignum objects. $a should +# be the larger of the two values, and the two values should be +# coprime. + +sub modular_multi_inverse { + my $a = shift; + my $b = shift; + + my $ctx = Crypt::OpenSSL::Bignum::CTX->new(); + my $x = Crypt::OpenSSL::Bignum->zero(); + my $y = Crypt::OpenSSL::Bignum->one(); + my $lastx = Crypt::OpenSSL::Bignum->one(); + my $lasty = Crypt::OpenSSL::Bignum->zero(); + + while (! $b->is_zero()) { + my ($quotient, $remainder) = $a->div($b, $ctx); + + $a = $b; + $b = $remainder; + + my $temp = $x; + $x = $lastx->sub($quotient->mul($x, $ctx)); + $lastx = $temp; + + $temp = $y; + $y = $lasty->sub($quotient->mul($y, $ctx)); + $lasty = $temp; + } + + if (!$a->is_one()) { + die "did this math wrong.\n"; + } + + return $lastx; +} + sub make_rsa_sec_key_body { my $key = shift; my $timestamp = shift; @@ -239,7 +280,7 @@ sub make_rsa_sec_key_body { my $secret_material = mpi_pack($d). mpi_pack($p). mpi_pack($q). - mpi_pack($c); + mpi_pack(modular_multi_inverse($p, $q)); # FIXME: according to Crypt::OpenSSL::RSA, $c is 1/q mod p; but # according to sec 5.5.3 of RFC 4880, this last argument should @@ -254,7 +295,7 @@ sub make_rsa_sec_key_body { mpi_pack($e). pack('C', 0). # seckey material is not encrypted -- see RFC 4880 sec 5.5.3 $secret_material. - simple_checksum($secret_material); + pack('n', simple_checksum($secret_material)); } # expects an RSA key (public or private) and a timestamp @@ -406,7 +447,7 @@ my $sig_body = mpi_pack($sig); print - make_packet($packet_types->{pubkey}, $pubkey). + make_packet($packet_types->{seckey}, $seckey). make_packet($packet_types->{uid}, $uid). make_packet($packet_types->{sig}, $sig_body); -- cgit v1.2.3 From c2e9fab1b3e8f0c254a41e7875d4aaf9bb5b2419 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 11 Jan 2009 23:05:44 -0500 Subject: pem2openpgp: cleaning up some comments, not fetching unnecessary parameters from OpenSSL. --- src/keytrans/pem2openpgp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index c74ca25..4cc6f1d 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -275,18 +275,18 @@ sub make_rsa_sec_key_body { my $timestamp = shift; # we're not using $a and $b, but we need them to get to $c. - my ($n, $e, $d, $p, $q, $a, $b, $c) = $key->get_key_parameters(); + my ($n, $e, $d, $p, $q) = $key->get_key_parameters(); my $secret_material = mpi_pack($d). mpi_pack($p). mpi_pack($q). mpi_pack(modular_multi_inverse($p, $q)); - # FIXME: according to Crypt::OpenSSL::RSA, $c is 1/q mod p; but - # according to sec 5.5.3 of RFC 4880, this last argument should - # instead be: u, the multiplicative inverse of p, mod q. i don't - # see a simple way to generate this number from the perl module - # directly yet. + # according to Crypt::OpenSSL::RSA, the closest value we can get out + # of get_key_parameters is 1/q mod p; but according to sec 5.5.3 of + # RFC 4880, we're actually looking for u, the multiplicative inverse + # of p, mod q. This is why we're calculating the value directly + # with modular_multi_inverse. return pack('CN', 4, $timestamp). -- cgit v1.2.3 From 71afa5c8ef69365ee7db26d865f277f053198739 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 11 Jan 2009 23:27:41 -0500 Subject: pem2openpgp: reorganization, cleanup of comments, adding a warning about secret material on stdout --- src/keytrans/pem2openpgp | 121 ++++++++++++++++++++++++++--------------------- 1 file changed, 68 insertions(+), 53 deletions(-) (limited to 'src/keytrans/pem2openpgp') diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 4cc6f1d..3d9f6f8 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -1,8 +1,12 @@ #!/usr/bin/perl -w -T # pem2openpgp: take a PEM-encoded RSA private-key on standard input, a -# User ID as the first argument, and generate an OpenPGP certificate -# from it. +# User ID as the first argument, and generate an OpenPGP secret key +# and certificate from it. + +# WARNING: the secret key material *will* appear on stdout (albeit in +# OpenPGP form) -- if you redirect stdout to a file, make sure the +# permissions on that file are appropriately locked down! # Usage: @@ -160,6 +164,58 @@ my $keyserver_prefs = { nomodify => 0x80 # produce it, we need to produce key/value-swapped lookup tables as well. +########### Math/Utility Functions ############## + + +# see the bottom of page 43 of RFC 4880 +sub simple_checksum { + my $bytes = shift; + + return unpack("%32W*",$bytes) % 65536; +} + +# calculate the multiplicative inverse of a mod b this is euclid's +# extended algorithm. For more information see: +# http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm the +# arguments here should be Crypt::OpenSSL::Bignum objects. $a should +# be the larger of the two values, and the two values should be +# coprime. + +sub modular_multi_inverse { + my $a = shift; + my $b = shift; + + my $ctx = Crypt::OpenSSL::Bignum::CTX->new(); + my $x = Crypt::OpenSSL::Bignum->zero(); + my $y = Crypt::OpenSSL::Bignum->one(); + my $lastx = Crypt::OpenSSL::Bignum->one(); + my $lasty = Crypt::OpenSSL::Bignum->zero(); + + while (! $b->is_zero()) { + my ($quotient, $remainder) = $a->div($b, $ctx); + + $a = $b; + $b = $remainder; + + my $temp = $x; + $x = $lastx->sub($quotient->mul($x, $ctx)); + $lastx = $temp; + + $temp = $y; + $y = $lasty->sub($quotient->mul($y, $ctx)); + $lasty = $temp; + } + + if (!$a->is_one()) { + die "did this math wrong.\n"; + } + + return $lastx; +} + + +############ OpenPGP formatting functions ############ + # make an old-style packet out of the given packet type and body. # old-style (see RFC 4880 section 4.2) sub make_packet { @@ -210,13 +266,6 @@ sub mpi_pack { return pack('n', $mpilen).$val; } -# see the bottom of page 43 of RFC 4880 -sub simple_checksum { - my $bytes = shift; - - return unpack("%32W*",$bytes) % 65536; -} - # FIXME: genericize these to accept either RSA or DSA keys: sub make_rsa_pub_key_body { my $key = shift; @@ -231,45 +280,6 @@ sub make_rsa_pub_key_body { mpi_pack($e); } -# calculate the multiplicative inverse of a mod b this is euclid's -# extended algorithm. For more information see: -# http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm the -# arguments here should be Crypt::OpenSSL::Bignum objects. $a should -# be the larger of the two values, and the two values should be -# coprime. - -sub modular_multi_inverse { - my $a = shift; - my $b = shift; - - my $ctx = Crypt::OpenSSL::Bignum::CTX->new(); - my $x = Crypt::OpenSSL::Bignum->zero(); - my $y = Crypt::OpenSSL::Bignum->one(); - my $lastx = Crypt::OpenSSL::Bignum->one(); - my $lasty = Crypt::OpenSSL::Bignum->zero(); - - while (! $b->is_zero()) { - my ($quotient, $remainder) = $a->div($b, $ctx); - - $a = $b; - $b = $remainder; - - my $temp = $x; - $x = $lastx->sub($quotient->mul($x, $ctx)); - $lastx = $temp; - - $temp = $y; - $y = $lasty->sub($quotient->mul($y, $ctx)); - $lasty = $temp; - } - - if (!$a->is_one()) { - die "did this math wrong.\n"; - } - - return $lastx; -} - sub make_rsa_sec_key_body { my $key = shift; my $timestamp = shift; @@ -336,23 +346,28 @@ my $hash_algo = pack('C', $digests->{sha1}); # 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? +# this script more than once against the same key (because the +# timestamps will differ). How can we prevent this? -# could an environment variable (if set) override the current time? +# could an environment variable (if set) override the current time, to +# be able to create a standard key? If we read the key from a file +# instead of stdin, should we use the creation time on the file? my $timestamp = time(); my $creation_time_packet = pack('CCN', 5, $subpacket_types->{sig_creation_time}, $timestamp); # FIXME: HARDCODED: what if someone wants to select a different set of -# usage flags? For now, we do only authentication. +# usage flags? For now, we do only authentication because that's what +# monkeysphere needs. my $usage_packet = pack('CCC', 2, $subpacket_types->{usage_flags}, $usage_flags->{authenticate}); # 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). +# short (but good for testing). The user ought to be able to decide +# this directly, rather than having to do "monkeysphere-server +# extend-key". my $expires_in = 86400*2; my $expiration_packet = pack('CCN', 5, $subpacket_types->{key_expiration_time}, $expires_in); -- cgit v1.2.3