diff options
-rwxr-xr-x | src/keytrans/pem2openpgp | 95 | ||||
-rwxr-xr-x | tests/basic | 38 | ||||
-rw-r--r-- | tests/common | 29 | ||||
-rwxr-xr-x | tests/keytrans | 88 | ||||
-rw-r--r-- | website/news/plans-for-the-bezoar.mdwn | 45 |
5 files changed, 239 insertions, 56 deletions
diff --git a/src/keytrans/pem2openpgp b/src/keytrans/pem2openpgp index 3d9f6f8..c765002 100755 --- a/src/keytrans/pem2openpgp +++ b/src/keytrans/pem2openpgp @@ -38,6 +38,11 @@ my $uid = shift; # hostname_long() from Sys::Hostname::Long ? +my $old_format_packet_lengths = { one => 0, + two => 1, + four => 2, + indeterminate => 3, +}; # see RFC 4880 section 9.1 (ignoring deprecated algorithms for now) my $asym_algos = { rsa => 1, @@ -185,12 +190,18 @@ sub modular_multi_inverse { my $a = shift; my $b = shift; + + my $origdivisor = $b->copy(); + 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(); + my $finalquotient; + my $finalremainder; + while (! $b->is_zero()) { my ($quotient, $remainder) = $a->div($b, $ctx); @@ -210,7 +221,12 @@ sub modular_multi_inverse { die "did this math wrong.\n"; } - return $lastx; + # let's make sure that we return a positive value because RFC 4880, + # section 3.2 only allows unsigned values: + + ($finalquotient, $finalremainder) = $lastx->add($origdivisor)->div($origdivisor, $ctx); + + return $finalremainder; } @@ -221,26 +237,37 @@ sub modular_multi_inverse { sub make_packet { my $type = shift; my $body = shift; + my $options = shift; my $len = length($body); + my $pseudolen = $len; + + # if the caller wants to use at least N octets of packet length, + # pretend that we're using that many. + if (defined $options && defined $options->{'packet_length'}) { + $pseudolen = 2**($options->{'packet_length'} * 8) - 1; + } + if ($pseudolen < $len) { + $pseudolen = $len; + } my $lenbytes; my $lencode; - if ($len < 2**8) { - $lenbytes = 0; + if ($pseudolen < 2**8) { + $lenbytes = $old_format_packet_lengths->{one}; $lencode = 'C'; - } elsif ($len < 2**16) { - $lenbytes = 1; + } elsif ($pseudolen < 2**16) { + $lenbytes = $old_format_packet_lengths->{two}; $lencode = 'n'; - } elsif ($len < 2**31) { + } elsif ($pseudolen < 2**31) { ## not testing against full 32 bits because i don't want to deal ## with potential overflow. - $lenbytes = 2; + $lenbytes = $old_format_packet_lengths->{four}; $lencode = 'N'; } else { ## what the hell do we do here? - $lenbytes = 3; + $lenbytes = $old_format_packet_lengths->{indeterminate}; $lencode = ''; } @@ -287,10 +314,12 @@ sub make_rsa_sec_key_body { # we're not using $a and $b, but we need them to get to $c. my ($n, $e, $d, $p, $q) = $key->get_key_parameters(); + my $c3 = modular_multi_inverse($p, $q); + my $secret_material = mpi_pack($d). mpi_pack($p). mpi_pack($q). - mpi_pack(modular_multi_inverse($p, $q)); + mpi_pack($c3); # 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 @@ -349,27 +378,43 @@ my $hash_algo = pack('C', $digests->{sha1}); # 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, to +# this environment variable (if set) overrides 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 $timestamp = 0; +if (defined $ENV{PEM2OPENPGP_TIMESTAMP}) { + $timestamp = ($ENV{PEM2OPENPGP_TIMESTAMP} + 0); +} else { + $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 because that's what -# monkeysphere needs. -my $usage_packet = pack('CCC', 2, $subpacket_types->{usage_flags}, $usage_flags->{authenticate}); +my $flags = 0; +if (! defined $ENV{PEM2OPENPGP_USAGE_FLAGS}) { + $flags = $usage_flags->{certify}; +} else { + my @ff = split(",", $ENV{PEM2OPENPGP_USAGE_FLAGS}); + foreach my $f (@ff) { + if (! defined $usage_flags->{$f}) { + die "No such flag $f"; + } + $flags |= $usage_flags->{$f}; + } +} + +my $usage_packet = pack('CCC', 2, $subpacket_types->{usage_flags}, $flags); -# 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). 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); +# how should we determine how far off to set the expiration date? +# default is no expiration. Specify the timestamp in seconds from the +# key creation. +my $expiration_packet = ''; +if (defined $ENV{PEM2OPENPGP_EXPIRATION}) { + my $expires_in = $ENV{PEM2OPENPGP_EXPIRATION} + 0; + $expiration_packet = pack('CCN', 5, $subpacket_types->{key_expiration_time}, $expires_in); +} # prefer AES-256, AES-192, AES-128, CAST5, 3DES: @@ -426,7 +471,10 @@ my $sig_data_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); +# this is for signing. it needs to be an old-style header with a +# 2-packet octet count. + +my $key_data = make_packet($packet_types->{pubkey}, $pubkey, {'packet_length'=>2}); # take the last 8 bytes of the fingerprint as the keyid: my $keyid = substr(fingerprint($rsa, $timestamp), 20 - 8, 8); @@ -449,7 +497,6 @@ my $datatosign = my $data_hash = Digest::SHA1::sha1_hex($datatosign); - my $issuer_packet = pack('CCa8', 9, $subpacket_types->{issuer}, $keyid); my $sig = Crypt::OpenSSL::Bignum->new_from_bin($rsa->sign($datatosign)); diff --git a/tests/basic b/tests/basic index a3d0b4f..5c6b4bb 100755 --- a/tests/basic +++ b/tests/basic @@ -17,6 +17,10 @@ set -e # piped commands should return the code of the first non-zero return set -o pipefail +export TESTDIR=$(dirname "$0") + +source "$TESTDIR"/common + ## make sure that the right tools are installed to run the test. the ## test has *more* requirements than plain ol' monkeysphere: which socat >/dev/null || { echo "You must have socat installed to run this test." ; exit 1; } @@ -72,34 +76,6 @@ ssh_test() { fi } -failed_cleanup() { - # FIXME: can we be more verbose here? - echo 'FAILED!' - read -p "press enter to cleanup and remove tmp:" - - cleanup -} - -get_gpg_prng_arg() { - if (gpg --quick-random --version >/dev/null 2>&1) ; then - echo quick-random - elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then - echo debug-quick-random - fi -} - -cleanup() { - echo "### removing temp dir..." - rm -rf "$TEMPDIR" - - if [ "$SSHD_PID" ] ; then - echo "### killing off lingering sshd..." - kill "$SSHD_PID" - fi - - wait -} - SSHD_PID= ## setup trap @@ -112,15 +88,13 @@ trap failed_cleanup EXIT ## set up some variables to ensure that we're operating strictly in ## the tests, not system-wide: -export TESTDIR=$(dirname "$0") - # make temp dir TEMPDIR="$TESTDIR"/tmp if [ -e "$TEMPDIR" ] ; then echo "tempdir '$TEMPDIR' already exists." exit 1 fi -mkdir "$TEMPDIR" +mkdir -p "$TEMPDIR" # Use the local copy of executables first, instead of system ones. # This should help us test without installing. @@ -154,7 +128,7 @@ cp -a "$TESTDIR"/home/admin "$TEMPDIR"/ cp -a "$TESTDIR"/home/testuser "$TEMPDIR"/ # set up environment for testuser -TESTHOME="$TEMPDIR"/testuser +export TESTHOME="$TEMPDIR"/testuser export GNUPGHOME="$TESTHOME"/.gnupg export SSH_ASKPASS="$TESTHOME"/.ssh/askpass export MONKEYSPHERE_HOME="$TESTHOME"/.monkeysphere diff --git a/tests/common b/tests/common new file mode 100644 index 0000000..adc96a2 --- /dev/null +++ b/tests/common @@ -0,0 +1,29 @@ +# -*-shell-script-*- + +failed_cleanup() { + # FIXME: can we be more verbose here? + echo 'FAILED!' + read -p "press enter to cleanup and remove tmp:" + + cleanup +} + +get_gpg_prng_arg() { + if (gpg --quick-random --version >/dev/null 2>&1) ; then + echo quick-random + elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then + echo debug-quick-random + fi +} + +cleanup() { + echo "### removing temp dir..." + rm -rf "$TEMPDIR" + + if [ "$SSHD_PID" ] ; then + echo "### killing off lingering sshd..." + kill "$SSHD_PID" + fi + + wait +} diff --git a/tests/keytrans b/tests/keytrans new file mode 100755 index 0000000..285d17b --- /dev/null +++ b/tests/keytrans @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +# Tests to ensure that the monkeysphere is working + +# Authors: +# Daniel Kahn Gillmor <dkg@fifthhorseman.net> +# Jameson Rollins <jrollins@fifthhorseman.net> +# Micah Anderson <micah@riseup.net> +# +# Copyright: 2008-2009 +# License: GPL v3 or later + +# these tests should all be able to run as a non-privileged user. + +# all subcommands in this script should complete without failure: +set -e +# piped commands should return the code of the first non-zero return +set -o pipefail + +export TESTDIR=$(dirname "$0") + +source "$TESTDIR"/common + +## setup trap +trap failed_cleanup EXIT + +###################################################################### +### SETUP VARIABLES + +## set up some variables to ensure that we're operating strictly in +## the tests, not system-wide: + +# make temp dir +TEMPDIR="$TESTDIR"/tmp +if [ -e "$TEMPDIR" ] ; then + echo "tempdir '$TEMPDIR' already exists." + exit 1 +fi +mkdir -p "$TEMPDIR" + +# Use the local copy of executables first, instead of system ones. +# This should help us test without installing. +export PATH="$TESTDIR"/../src/keytrans:"$PATH" + +###################################################################### +### TEST KEYTRANS + +echo "##################################################" +echo "### generating openpgp key..." +export GNUPGHOME="$TEMPDIR" +chmod 700 "$TEMPDIR" +# generate a key +gpg --batch --$(get_gpg_prng_arg) --gen-key <<EOF +Key-Type: RSA +Key-Length: 1024 +Key-Usage: sign +Name-Real: testtest +Expire-Date: 0 + +%commit +%echo done +EOF + +echo "##################################################" +echo "### retrieving key timestamp..." +timestamp=$(gpg --list-key --with-colons --fixed-list-mode | \ + grep ^pub: | cut -d: -f6) + +echo "##################################################" +echo "### exporting key to ssh file..." +gpg --export-secret-key | openpgp2ssh > \ + "$TEMPDIR"/test.pem + +echo "##################################################" +echo "### reconvert key, and compare to key in gpg keyring..." +diff -u \ + <(gpg --export-secret-key | hd) \ + <(PEM2OPENPGP_USAGE_FLAGS=sign,certify \ + PEM2OPENPGP_TIMESTAMP="$timestamp" pem2openpgp testtest < \ + "$TEMPDIR"/test.pem | hd ) + +trap - EXIT + +echo "##################################################" +echo " Monkeysphere keytrans test completed successfully!" +echo "##################################################" + +cleanup diff --git a/website/news/plans-for-the-bezoar.mdwn b/website/news/plans-for-the-bezoar.mdwn new file mode 100644 index 0000000..0fb2c5b --- /dev/null +++ b/website/news/plans-for-the-bezoar.mdwn @@ -0,0 +1,45 @@ +[[meta title="Plans for The Golden Bezoar"]] + +A workday with several Monkeysphere contributors on 2009-01-31 +resulted in a significant reorganization of the project in several +areas, primarily driven by the realization that there are two +fundamentally different concepts on the server side: + +* publishing host keys via the Web-of-Trust (WoT), and +* authenticating users via the WoT. + +For simplicity and clarity, those two concepts should be independent +from each other, but earlier releases of the Monkeysphere tangled the +two up together more than we probably should have. + +So the next release, version 0.23 (a.k.a. *The Golden Bezoar*) will +have the following significant changes: + +* __user interface__: `/usr/sbin/monkeysphere-server` is no more, and + its functionality will be split out into + `/usr/sbin/monkeysphere-host` (for functionality dealing with + publishing the ssh host key through the WoT) and + `/usr/sbin/monkeysphere-authentication` (for functionality dealing + with authenticating users via the + WoT). `/usr/bin/monkeysphere-ssh-proxycommand` has been folded into + `/usr/bin/monkeysphere` itself as a new subcommand. + +* __code__: the subfunctions are now stored in their own separate + files, and sourced as-needed by the three top-level commands. The + test suite has also been re-written to reflect the above UI changes. + +* __documentation__: in addition to making the man pages reflect the + above UI changes, we're rewriting the "getting started" + [documentation](/doc/) to use the conceptually-cleaner distinctions + above. + +* __data storage__: `/var/lib/monkeysphere` itself has been + re-organized with the aim of keeping the host/authentication + distinction clear, simplifying the internal use of `gpg`, and + facilitating privilege-separated access. + +*The Golden Bezoar* will also feature the ability to painlessly +publish your current ssh host key to the WoT without needing to re-key +the server. If you're considering adopting the Monkeysphere in the +near future, we recommend waiting for 0.23 to be released, as it +should be conceptually clearer and easier to use. |