From 691e5d2ec8efeb4d77b17b1ad852fdbec31ce136 Mon Sep 17 00:00:00 2001 From: Jameson Graef Rollins Date: Tue, 16 Dec 2008 22:00:36 -0500 Subject: get rid of getopts. add checks for root user, and better checking of presence of host key. --- src/monkeysphere | 18 ++---- src/monkeysphere-server | 149 +++++++++++++++++++++++++----------------------- 2 files changed, 84 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/monkeysphere b/src/monkeysphere index 523ddfe..c003706 100755 --- a/src/monkeysphere +++ b/src/monkeysphere @@ -63,15 +63,6 @@ gen_subkey(){ keyExpire= # get options - TEMP=$(PATH="/usr/local/bin:$PATH" getopt -o l:e: -l length:,expire: -n "$PGRM" -- "$@") || failure "getopt failed! Does your getopt support GNU-style long options?" - - if [ $? != 0 ] ; then - exit 1 - fi - - # Note the quotes around `$TEMP': they are essential! - eval set -- "$TEMP" - while true ; do case "$1" in -l|--length) @@ -82,10 +73,11 @@ gen_subkey(){ keyExpire="$2" shift 2 ;; - --) - shift - ;; - *) + *) + if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then + failure "Unknown option '$1'. +Type '$PGRM help' for usage." + fi break ;; esac diff --git a/src/monkeysphere-server b/src/monkeysphere-server index c4f6985..7d7578d 100755 --- a/src/monkeysphere-server +++ b/src/monkeysphere-server @@ -117,40 +117,59 @@ gpg_authentication() { su_monkeysphere_user "gpg $@" } -# function to check for host secret keys -# fails if host sec key exists, exits true otherwise -check_host_keyring() { - if ! gpg_host --list-secret-keys --fingerprint \ - --with-colons --fixed-list-mode 2>/dev/null | grep -q '^sec:' ; then +# check if user is root +is_root() { + [ $(id -u 2>/dev/null) = '0' ] +} - failure "You don't appear to have a Monkeysphere host key on this server. Please run 'monkeysphere-server gen-key' first." - fi +# check that user is root, for functions that require root access +check_user() { + is_root || failure "You must be root to run this command." } # output just key fingerprint fingerprint_server_key() { + # set the pipefail option so functions fails if can't read sec key + set -o pipefail + gpg_host --list-secret-keys --fingerprint \ --with-colons --fixed-list-mode 2> /dev/null | \ - grep '^fpr:' | head -1 | cut -d: -f10 + grep '^fpr:' | head -1 | cut -d: -f10 2>/dev/null +} + +# function to check for host secret key +check_host_keyring() { + fingerprint_server_key >/dev/null \ + || failure "You don't appear to have a Monkeysphere host key on this server. Please run 'monkeysphere-server gen-key' first." } # output key information show_server_key() { - local fingerprint - local tmpkey + local fingerprintPGP + local fingerprintSSH + local ret=0 - fingerprint=$(fingerprint_server_key) - gpg_authentication "--fingerprint --list-key --list-options show-unusable-uids $fingerprint" - - # do some crazy "Here Strings" redirection to get the key to - # ssh-keygen, since it doesn't read from stdin cleanly - echo -n "ssh fingerprint: " - ssh-keygen -l -f /dev/stdin \ - <<<$(gpg_authentication "--export $fingerprint" | \ - openpgp2ssh "$fingerprint" 2>/dev/null) | \ - awk '{ print $1, $2, $4 }' - echo -n "OpenPGP fingerprint: " - echo "$fingerprint" + # FIXME: you shouldn't have to be root to see the host key fingerprint + if is_root ; then + check_host_keyring + fingerprintPGP=$(fingerprint_server_key) + gpg_authentication "--fingerprint --list-key --list-options show-unusable-uids $fingerprintPGP" 2>/dev/null + echo "OpenPGP fingerprint: $fingerprintPGP" + else + log info "You must be root to see host OpenPGP fingerprint." + ret='1' + fi + + if [ -f "${SYSDATADIR}/ssh_host_rsa_key.pub" ] ; then + fingerprintSSH=$(ssh-keygen -l -f "${SYSDATADIR}/ssh_host_rsa_key.pub" | \ + awk '{ print $1, $2, $4 }') + echo "ssh fingerprint: $fingerprintSSH" + else + log info "SSH host key not found." + ret='1' + fi + + return $ret } # update authorized_keys for users @@ -311,15 +330,6 @@ gen_key() { revoker= # get options - TEMP=$(PATH="/usr/local/bin:$PATH" getopt -o e:l:r -l expire:,length:,revoker: -n "$PGRM" -- "$@") || failure "getopt failed! Does your getopt support GNU-style long options?" - - if [ $? != 0 ] ; then - exit 1 - fi - - # Note the quotes around `$TEMP': they are essential! - eval set -- "$TEMP" - while true ; do case "$1" in -l|--length) @@ -334,10 +344,11 @@ gen_key() { revoker="$2" shift 2 ;; - --) - shift - ;; - *) + *) + if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then + failure "Unknown option '$1'. +Type '$PGRM help' for usage." + fi break ;; esac @@ -346,33 +357,29 @@ gen_key() { hostName=${1:-$(hostname -f)} userID="ssh://${hostName}" - # check for presense of key with user ID + # check for presense of secret key # FIXME: is this the proper test to be doing here? - if gpg_host --list-key ="$userID" > /dev/null 2>&1 ; then - failure "Key for '$userID' already exists" - fi + fingerprint_server_key >/dev/null \ + && failure "A key for this host already exists." # prompt about key expiration if not specified keyExpire=$(get_gpg_expiration "$keyExpire") # set key parameters - keyParameters=$(cat < "${SYSDATADIR}/ssh_host_rsa_key.pub.gpg" log info "SSH host public key in OpenPGP form: ${SYSDATADIR}/ssh_host_rsa_key.pub.gpg" + + # show info about new key + show_server_key } # extend the lifetime of a host key: @@ -478,7 +485,7 @@ $userID save EOF - ) +) # execute edit-key script if echo "$adduidCommand" | \ @@ -778,15 +785,6 @@ add_certifier() { depth=1 # get options - TEMP=$(PATH="/usr/local/bin:$PATH" getopt -o n:t:d: -l domain:,trust:,depth: -n "$PGRM" -- "$@") || failure "getopt failed! Does your getopt support GNU-style long options?" - - if [ $? != 0 ] ; then - exit 1 - fi - - # Note the quotes around `$TEMP': they are essential! - eval set -- "$TEMP" - while true ; do case "$1" in -n|--domain) @@ -801,10 +799,11 @@ add_certifier() { depth="$2" shift 2 ;; - --) - shift - ;; - *) + *) + if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then + failure "Unknown option '$1'. +Type '$PGRM help' for usage." + fi break ;; esac @@ -1001,59 +1000,69 @@ shift case $COMMAND in 'update-users'|'update-user'|'u') + check_user check_host_keyring update_users "$@" ;; 'gen-key'|'g') + check_user gen_key "$@" ;; 'extend-key'|'e') + check_user check_host_keyring extend_key "$@" ;; 'add-hostname'|'add-name'|'n+') + check_user check_host_keyring add_hostname "$@" ;; 'revoke-hostname'|'revoke-name'|'n-') + check_user check_host_keyring revoke_hostname "$@" ;; 'show-key'|'show'|'s') - check_host_keyring show_server_key ;; 'publish-key'|'publish'|'p') + check_user check_host_keyring publish_server_key ;; 'diagnostics'|'d') + check_user diagnostics ;; 'add-identity-certifier'|'add-id-certifier'|'add-certifier'|'c+') + check_user check_host_keyring add_certifier "$@" ;; 'remove-identity-certifier'|'remove-id-certifier'|'remove-certifier'|'c-') + check_user check_host_keyring remove_certifier "$@" ;; 'list-identity-certifiers'|'list-id-certifiers'|'list-certifiers'|'list-certifier'|'c') + check_user check_host_keyring list_certifiers "$@" ;; 'gpg-authentication-cmd') + check_user gpg_authentication_cmd "$@" ;; -- cgit v1.2.3 From 8e582f8c7cabe19275bc71d6093c9d07bf38b3f9 Mon Sep 17 00:00:00 2001 From: Jameson Graef Rollins Date: Sun, 28 Dec 2008 17:09:44 -0500 Subject: added version output option --- Makefile | 1 + packaging/debian/changelog | 3 ++- src/common | 3 +++ src/monkeysphere | 5 +++++ src/monkeysphere-server | 5 +++++ 5 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/Makefile b/Makefile index 7493b1f..e40c4b1 100755 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ tarball: clean debian-package: tarball tar xzf monkeysphere_$(MONKEYSPHERE_VERSION).orig.tar.gz + sed -i "s|__VERSION__|$(MONKEYSPHERE_VERSION)|g" monkeysphere-$(MONKEYSPHERE_VERSION)/src/common cp -a packaging/debian monkeysphere-$(MONKEYSPHERE_VERSION) (cd monkeysphere-$(MONKEYSPHERE_VERSION) && debuild -uc -us) rm -rf monkeysphere-$(MONKEYSPHERE_VERSION) diff --git a/packaging/debian/changelog b/packaging/debian/changelog index 55f0aaf..45deaab 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -5,8 +5,9 @@ monkeysphere (0.23~pre-1) UNRELEASED; urgency=low functions that require it to be there. - add checks for root users, for functions where it is required. - get rid of getopts. + - added version output option - -- Jameson Graef Rollins Tue, 16 Dec 2008 15:26:53 -0500 + -- Jameson Graef Rollins Sun, 28 Dec 2008 15:54:21 -0500 monkeysphere (0.22-1) unstable; urgency=low diff --git a/src/common b/src/common index f6000d3..eb3a083 100644 --- a/src/common +++ b/src/common @@ -19,6 +19,9 @@ SYSCONFIGDIR=${MONKEYSPHERE_SYSCONFIGDIR:-"/etc/monkeysphere"} export SYSCONFIGDIR +# monkeysphere version +VERSION=__VERSION__ + ######################################################################## ### UTILITY FUNCTIONS diff --git a/src/monkeysphere b/src/monkeysphere index c003706..98531d2 100755 --- a/src/monkeysphere +++ b/src/monkeysphere @@ -45,6 +45,7 @@ subcommands: --length (-l) BITS key length in bits (2048) --expire (-e) EXPIRE date to expire subkey-to-ssh-agent (s) store authentication subkey in ssh-agent + version (v) show version number help (h,?) this help EOF @@ -365,6 +366,10 @@ case $COMMAND in subkey_to_ssh_agent "$@" ;; + 'version'|'v') + echo "$VERSION" + ;; + '--help'|'help'|'-h'|'h'|'?') usage ;; diff --git a/src/monkeysphere-server b/src/monkeysphere-server index 7d7578d..ba3fa8d 100755 --- a/src/monkeysphere-server +++ b/src/monkeysphere-server @@ -66,6 +66,7 @@ subcommands: gpg-authentication-cmd CMD gnupg-authentication command + version (v) show version number help (h,?) this help EOF @@ -1066,6 +1067,10 @@ case $COMMAND in gpg_authentication_cmd "$@" ;; + 'version'|'v') + echo "$VERSION" + ;; + '--help'|'help'|'-h'|'h'|'?') usage ;; -- cgit v1.2.3 From 47ab7d6bcb9922a984f103a9385f068e0fb3c4bc Mon Sep 17 00:00:00 2001 From: Jameson Graef Rollins Date: Tue, 30 Dec 2008 20:22:22 -0500 Subject: clean up option parsing and key checking in gen_key function, including adding checking for validity of existing authentication subkeys. --- packaging/debian/changelog | 4 ++- src/monkeysphere | 84 ++++++++++++++++++++++++++++------------------ 2 files changed, 54 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/packaging/debian/changelog b/packaging/debian/changelog index 45deaab..a282c58 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -6,8 +6,10 @@ monkeysphere (0.23~pre-1) UNRELEASED; urgency=low - add checks for root users, for functions where it is required. - get rid of getopts. - added version output option + - check that existing authentication keys are valid in gen_key + function. - -- Jameson Graef Rollins Sun, 28 Dec 2008 15:54:21 -0500 + -- Jameson Graef Rollins Tue, 30 Dec 2008 20:21:16 -0500 monkeysphere (0.22-1) unstable; urgency=low diff --git a/src/monkeysphere b/src/monkeysphere index 98531d2..5444cb0 100755 --- a/src/monkeysphere +++ b/src/monkeysphere @@ -84,42 +84,59 @@ Type '$PGRM help' for usage." esac done - if [ -z "$1" ] ; then - # find all secret keys - keyID=$(gpg --with-colons --list-secret-keys | grep ^sec | cut -f5 -d: | sort -u) - # if multiple sec keys exist, fail - if (( $(echo "$keyID" | wc -l) > 1 )) ; then - echo "Multiple secret keys found:" - echo "$keyID" + case "$#" in + 0) + gpgSecOut=$(gpg --quiet --fixed-list-mode --list-secret-keys --with-colons 2>/dev/null | egrep '^sec:') + ;; + 1) + gpgSecOut=$(gpg --quiet --fixed-list-mode --list-secret-keys --with-colons "$1" | egrep '^sec:') || failure + ;; + *) + failure "You must specify only a single primary key ID." + ;; + esac + + # check that only a single secret key was found + case $(echo "$gpgSecOut" | grep -c '^sec:') in + 0) + failure "No secret keys found. Create an OpenPGP key with the following command: + gpg --gen-key" + ;; + 1) + keyID=$(echo "$gpgSecOut" | cut -d: -f5) + ;; + *) + echo "Multiple primary secret keys found:" + echo "$gpgSecOut" | cut -d: -f5 failure "Please specify which primary key to use." + ;; + esac + + # check that a valid authentication key does not already exist + IFS=$'\n' + for line in $(gpg --quiet --fixed-list-mode --list-keys --with-colons "$keyID") ; do + type=$(echo "$line" | cut -d: -f1) + validity=$(echo "$line" | cut -d: -f2) + usage=$(echo "$line" | cut -d: -f12) + + # look at keys only + if [ "$type" != 'pub' -a "$type" != 'sub' ] ; then + continue fi - else - keyID="$1" - fi - if [ -z "$keyID" ] ; then - failure "You have no secret key available. You should create an OpenPGP -key before joining the monkeysphere. You can do this with: - gpg --gen-key" - fi - - # get key output, and fail if not found - gpgOut=$(gpg --quiet --fixed-list-mode --list-secret-keys --with-colons \ - "$keyID") || failure - - # fail if multiple sec lines are returned, which means the id - # given is not unique - if [ $(echo "$gpgOut" | grep -c '^sec:') -gt '1' ] ; then - failure "Key ID '$keyID' is not unique." - fi - - # prompt if an authentication subkey already exists - if echo "$gpgOut" | egrep "^(sec|ssb):" | cut -d: -f 12 | grep -q a ; then - echo "An authentication subkey already exists for key '$keyID'." - read -p "Are you sure you would like to generate another one? (y/N) " OK; OK=${OK:N} - if [ "${OK/y/Y}" != 'Y' ] ; then - failure "aborting." + # check for authentication capability + if ! check_capability "$usage" 'a' ; then + continue fi - fi + # if authentication key is valid, prompt to continue + if [ "$validity" = 'u' ] ; then + echo "A valid authentication key already exists for primary key '$keyID'." + read -p "Are you sure you would like to generate another one? (y/N) " OK; OK=${OK:N} + if [ "${OK/y/Y}" != 'Y' ] ; then + failure "aborting." + fi + break + fi + done # set subkey defaults # prompt about key expiration if not specified @@ -144,6 +161,7 @@ EOF (umask 077 && mkfifo "$fifoDir/pass") echo "$editCommands" | gpg --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --edit-key "$keyID" & + # FIXME: this needs to fail more gracefully if the passphrase is incorrect passphrase_prompt "Please enter your passphrase for $keyID: " "$fifoDir/pass" rm -rf "$fifoDir" -- cgit v1.2.3 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') 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') 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') 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') 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') 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') 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') 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') 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