Merge commit 'jrollins/master'
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Mon, 23 Jun 2008 21:02:33 +0000 (17:02 -0400)
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Mon, 23 Jun 2008 21:02:33 +0000 (17:02 -0400)
debian/control
man/man1/openpgp2ssh.1
src/keytrans/gnutls-helpers.c
src/keytrans/gnutls-helpers.h
src/keytrans/openpgp2ssh.c

index d4d25c6cd2c75e2db71df111117fd73e857efb96..85b2d3f9ebddfd07dcfc3e699660ea5c3e25ed44 100644 (file)
@@ -3,7 +3,7 @@ Section: net
 Priority: extra
 Maintainer: Daniel Kahn Gillmor <dkg-debian.org@fifthhorseman.net>
 Uploaders: Jameson Rollins <jrollins@fifthhorseman.net>
-Build-Depends: debhelper (>= 7.0), libgnutls-dev (>= 2.3.14)
+Build-Depends: debhelper (>= 7.0), libgnutls-dev (>= 2.4.0)
 Standards-Version: 3.8.0.1
 Homepage: http://cmrg.fifthhorseman.net/wiki/OpenPGPandSSH
 Dm-Upload-Allowed: yes
index bea1da50c4e1737fc30a7ba0ebee1aa8b163347f..6141ec52113a585427b70fbe337616bf28f23ea3 100644 (file)
@@ -19,11 +19,14 @@ SSH-style key on standard output.
 .Pp
 If the data on standard input contains no subkeys, you can invoke
 .Nm
-without arguments.  If the data on standard input contains
-multiple keys (e.g. a primary key and associated subkeys), you must
-specify a specific OpenPGP keyid (e.g. CCD2ED94D21739E9) or
-fingerprint as the first argument to indicate which key to export.
-The keyid must be exactly 16 hex characters.
+without arguments.  If the data on standard input contains multiple
+keys (e.g. a primary key and associated subkeys), you must specify a
+specific OpenPGP key identifier as the first argument to indicate
+which key to export.  The key ID is normally the 40 hex digit OpenPGP
+fingerprint of the key or subkey desired, but
+.Nm
+will accept as few as the last 8 digits of the fingerprint as a key
+ID.
 .Pp
 If the input contains an OpenPGP RSA or DSA public key, it will be
 converted to the OpenSSH-style single-line keystring, prefixed with
@@ -78,8 +81,9 @@ Secret key output is currently not passphrase-protected.
 .Nm
 currently cannot handle passphrase-protected secret keys on input.
 .Pp
-It would be nice to be able to use keyids shorter or longer than 16
-hex characters.
+Key identifiers consisting of an odd number of hex digits are not
+accepted.  Users who use a key ID with a standard length of 8, 16, or
+40 hex digits should not be affected by this.
 .Pp
 .Nm
 only acts on keys associated with the first primary key
index 5b4c46af187a2c662fb8ea97f66a54355cd1a5c2..7c4348dc0e8dd58048094ea475dc90cc82867634 100644 (file)
@@ -44,11 +44,11 @@ void init_keyid(gnutls_openpgp_keyid_t keyid) {
 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
 {
   assert(sizeof(out) >= 2*sizeof(keyid));
-  hex_print_data((char*)out, (const char*)keyid, sizeof(keyid));
+  hex_print_data((char*)out, (const unsigned char*)keyid, sizeof(keyid));
 }
 
 /* you must have twice as many bytes in the out buffer as in the in buffer */
-void hex_print_data(char* out, const char* in, size_t incount)
+void hex_print_data(char* out, const unsigned char* in, size_t incount)
 {
   static const char hex[16] = "0123456789ABCDEF";
   unsigned int inix = 0, outix = 0;
@@ -73,7 +73,6 @@ unsigned char hex2bin(unsigned char x) {
 
 void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
   unsigned int pkix = 0, outkix = 0;
-  
   while (pkix < sizeof(printable_keyid)) {
     unsigned hi = hex2bin(in[pkix]);
     unsigned lo = hex2bin(in[pkix + 1]);
@@ -92,6 +91,27 @@ void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
   }
 }
 
+unsigned int hexstring2bin(unsigned char* out, const char* in) {
+  unsigned int pkix = 0, outkix = 0;
+  int hi = 0; /* which nybble is it? */
+  
+  while (in[pkix]) {
+    unsigned char z = hex2bin(in[pkix]);
+    if (z != 0xff) {
+      if (!hi) {
+       if (out) out[outkix] = (z << 4);
+       hi = 1;
+      } else {
+       if (out) out[outkix] |= z;
+       hi = 0;
+       outkix++;
+      }
+      pkix++;
+    }      
+  }
+  return outkix*8 + (hi ? 4 : 0);
+}
+
 int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
   printable_keyid p;
   int ret;
index f196456c58cf58f3453975c56d5bba75d6216fe1..bf54af0f93ec1a3f75951a715d1c1874b3dfb5e2 100644 (file)
@@ -49,7 +49,18 @@ int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str);
 int convert_string_to_printable_keyid(printable_keyid out, const char* str);
 
 /* you must have twice as many bytes in the out buffer as in the in buffer */
-void hex_print_data(char* out, const char* in, size_t incount);
+void hex_print_data(char* out, const unsigned char* in, size_t incount);
+
+/* expects a null-terminated string as in, containing an even number
+   of hexadecimal characters.
+
+   returns length in *bits* of raw data as output. 
+
+   the out buffer must be at least half as long as in to hold the
+   output.  if out is NULL, no output will be generated, but the
+   length will still be returned.
+*/
+unsigned int hexstring2bin(unsigned char* out, const char* in);
 
 /* functions to get data into datum objects: */
 
index 92bdc1955c81e0cada4eb80a5177017f5b226575..5cc6cfa087e967e8e8afe49af2839073570fade6 100644 (file)
 
 
 /* FIXME: keyid should be const as well */
-int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, gnutls_openpgp_keyid_t* keyid) {
+int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_openpgp_privkey_t* pgp_privkey, const unsigned char* keyfpr, unsigned int fprlen) {
   gnutls_datum_t m, e, d, p, q, u, g, y, x;
   gnutls_pk_algorithm_t pgp_algo;
   unsigned int pgp_bits;
   int ret;
-  gnutls_openpgp_keyid_t curkeyid;
   int subkeyidx;
   int subkeycount;
   int found = 0;
+  unsigned char fingerprint[20];
+  size_t fingerprint_length = sizeof(fingerprint); 
 
   init_datum(&m);
   init_datum(&e);
@@ -61,20 +62,27 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open
     return 1;
   }
 
-  if ((keyid == NULL) && 
+  if ((keyfpr == NULL) && 
       (subkeycount > 0)) {
-    err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
+    err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
     return 1;
   }
 
-  if (keyid != NULL) {
-    ret = gnutls_openpgp_privkey_get_key_id(*pgp_privkey, curkeyid);
+  if (keyfpr != NULL) {
+    ret = gnutls_openpgp_privkey_get_fingerprint(*pgp_privkey, fingerprint, &fingerprint_length);
     if (ret) {
-      err(0,"Could not get keyid (error: %d)\n", ret);
+      err(0,"Could not get fingerprint (error: %d)\n", ret);
       return 1;
     }
+    if (fprlen > fingerprint_length) {
+      err(0, "Requested key identifier is longer than computed fingerprint\n");
+      return 1;
+    }
+    if (fingerprint_length > fprlen) {
+      err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+    }
   }
-  if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
+  if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
     /* we want to export the primary key: */
     err(0,"exporting primary key\n");
 
@@ -106,12 +114,19 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open
   } else {
     /* lets trawl through the subkeys until we find the one we want: */
     for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
-      ret = gnutls_openpgp_privkey_get_subkey_id(*pgp_privkey, subkeyidx, curkeyid);
+      ret = gnutls_openpgp_privkey_get_subkey_fingerprint(*pgp_privkey, subkeyidx, fingerprint, &fingerprint_length);
       if (ret) {
-       err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
+       err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
        return 1;
       }
-      if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
+      if (fprlen > fingerprint_length) {
+       err(0, "Requested key identifier is longer than computed fingerprint\n");
+       return 1;
+      }
+      if (fingerprint_length > fprlen) {
+       err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+      }
+      if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
        err(0,"exporting subkey index %d\n", subkeyidx);
 
        /* FIXME: this is almost identical to the block above for the
@@ -172,8 +187,7 @@ int convert_private_pgp_to_x509(gnutls_x509_privkey_t* output, const gnutls_open
 }
 
 /* FIXME: keyid should be const also */
-int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_openpgp_keyid_t* keyid) {
-  gnutls_openpgp_keyid_t curkeyid;
+int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, const unsigned char* keyfpr, size_t fprlen) {
   int ret;
   int subkeyidx;
   int subkeycount;
@@ -188,6 +202,9 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope
      algorithm name: */
   char output_data[20];
 
+  unsigned char fingerprint[20];
+  size_t fingerprint_length = sizeof(fingerprint); 
+
   /* variables for the output conversion: */
   int pipestatus;
   int pipefd, child_pid;
@@ -208,20 +225,27 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope
     return 1;
   }
 
-  if ((keyid == NULL) && 
+  if ((keyfpr == NULL) && 
       (subkeycount > 0)) {
-    err(0,"No keyid passed in, but there were %d keys to choose from\n", subkeycount + 1);
+    err(0,"No key identifier passed in, but there were %d keys to choose from\n", subkeycount + 1);
     return 1;
   }
 
-  if (keyid != NULL) {
-    ret = gnutls_openpgp_crt_get_key_id(*pgp_crt, curkeyid);
+  if (keyfpr != NULL) {
+    ret = gnutls_openpgp_crt_get_fingerprint(*pgp_crt, fingerprint, &fingerprint_length);
     if (ret) {
-      err(0,"Could not get keyid (error: %d)\n", ret);
+      err(0,"Could not get key fingerprint (error: %d)\n", ret);
       return 1;
     }
+    if (fprlen > fingerprint_length) {
+      err(0, "Requested key identifier is longer than computed fingerprint\n");
+      return 1;
+    }
+    if (fingerprint_length > fprlen) {
+      err(0, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+    }
   }
-  if ((keyid == NULL) || (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0)) {
+  if ((keyfpr == NULL) || (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0)) {
     /* we want to export the primary key: */
     err(0,"exporting primary key\n");
 
@@ -252,12 +276,19 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope
   } else {
     /* lets trawl through the subkeys until we find the one we want: */
     for (subkeyidx = 0; (subkeyidx < subkeycount) && !found; subkeyidx++) {
-      ret = gnutls_openpgp_crt_get_subkey_id(*pgp_crt, subkeyidx, curkeyid);
+      ret = gnutls_openpgp_crt_get_subkey_fingerprint(*pgp_crt, subkeyidx, fingerprint, &fingerprint_length);
       if (ret) {
-       err(0,"Could not get keyid of subkey with index %d (error: %d)\n", subkeyidx, ret);
+       err(0,"Could not get fingerprint of subkey with index %d (error: %d)\n", subkeyidx, ret);
        return 1;
       }
-      if (memcmp(*keyid, curkeyid, sizeof(gnutls_openpgp_keyid_t)) == 0) {
+      if (fprlen > fingerprint_length) {
+       err(0, "Requested key identifier is longer than computed fingerprint\n");
+       return 1;
+      }
+      if (fingerprint_length > fprlen) {
+       err(1, "Only comparing last %d bits of key fingerprint\n", fprlen*8);
+      }
+      if (memcmp(fingerprint + (fingerprint_length - fprlen), keyfpr, fprlen) == 0) {
        err(0,"exporting subkey index %d\n", subkeyidx);
 
        /* FIXME: this is almost identical to the block above for the
@@ -351,7 +382,7 @@ int emit_public_openssh_from_pgp(const gnutls_openpgp_crt_t* pgp_crt, gnutls_ope
 
 int main(int argc, char* argv[]) {
   gnutls_datum_t data;
-  int ret;
+  int ret = 0;
   gnutls_x509_privkey_t x509_privkey;
   gnutls_openpgp_privkey_t pgp_privkey;
   gnutls_openpgp_crt_t pgp_crt;
@@ -359,18 +390,54 @@ int main(int argc, char* argv[]) {
   char output_data[10240];
   size_t ods = sizeof(output_data);
   
-  gnutls_openpgp_keyid_t keyid;
-  gnutls_openpgp_keyid_t* use_keyid;
+  unsigned char * fingerprint = NULL;
+  size_t fpr_size;
+  char * prettyfpr = NULL;
 
   init_gnutls();
 
-  /* figure out what keyid we should be looking for: */
-  use_keyid = NULL;
+  /* figure out what key we should be looking for: */
   if (argv[1] != NULL) {
-    ret = convert_string_to_keyid(keyid, argv[1]);
-    if (ret != 0)
-      return ret;
-    use_keyid = &keyid;
+    if (strlen(argv[1]) > 81) {
+      /* safety check to avoid some sort of wacky overflow situation:
+        there's no reason that the key id should be longer than twice
+        a sane fingerprint (one byte between chars, and then another
+        two at the beginning and end) */
+      err(0, "Key identifier is way too long.  Please use at most 40 hex digits.\n");
+      return 1;
+    }
+
+    fpr_size = hexstring2bin(NULL, argv[1]);
+    if (fpr_size > 40*4) {
+      err(0, "Key identifier is longer than 40 hex digits\n");
+      return 1;
+    }
+    /* since fpr_size is initially in bits: */
+    if (fpr_size % 8 != 0) {
+      err(0, "Please provide an even number of hex digits for the key identifier\n");
+      return 1;
+    }
+    fpr_size /= 8;
+
+    fingerprint = malloc(sizeof(unsigned char) * fpr_size);
+    bzero(fingerprint, sizeof(unsigned char) * fpr_size);
+    hexstring2bin(fingerprint, argv[1]);
+
+    prettyfpr = malloc(sizeof(unsigned char)*fpr_size*2 + 1);
+    if (prettyfpr != NULL) {
+      hex_print_data(prettyfpr, fingerprint, fpr_size);
+      prettyfpr[sizeof(unsigned char)*fpr_size*2] = '\0';
+      err(1, "searching for key with fingerprint '%s'\n", prettyfpr);
+      free(prettyfpr);
+    }
+
+    if (fpr_size < 4) {
+      err(0, "You MUST provide at least 8 hex digits in any key identifier\n");
+      return 1;
+    }
+    if (fpr_size < 8)
+      err(0, "You should provide at least 16 hex digits in any key identifier (proceeding with %d digits anyway)\n", fpr_size*2);
+   
   }
 
   
@@ -397,7 +464,7 @@ int main(int argc, char* argv[]) {
       return 1;
     }
     
-    ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, use_keyid);
+    ret = convert_private_pgp_to_x509(&x509_privkey, &pgp_privkey, fingerprint, fpr_size);
 
     gnutls_openpgp_privkey_deinit(pgp_privkey);
     if (ret)
@@ -423,7 +490,7 @@ int main(int argc, char* argv[]) {
       /* we're dealing with a public key */
       err(0,"Translating public key\n");
 
-      ret = emit_public_openssh_from_pgp(&pgp_crt, use_keyid);
+      ret = emit_public_openssh_from_pgp(&pgp_crt, fingerprint, fpr_size);
       
     } else {
       /* we have no idea what kind of key this is at all anyway! */
@@ -433,5 +500,6 @@ int main(int argc, char* argv[]) {
   }
 
   gnutls_global_deinit();
+  free(fingerprint);
   return 0;
 }