openpgp2ssh can now accept arbitrary-length key IDs (from the trivial
authorDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Mon, 23 Jun 2008 21:02:15 +0000 (17:02 -0400)
committerDaniel Kahn Gillmor <dkg@fifthhorseman.net>
Mon, 23 Jun 2008 21:02:15 +0000 (17:02 -0400)
8 hex digit key IDs to 40 hex digits of a full fingerprint).

This moves our build dependency on gnutls to 2.4.0, which includes
subkey fingerprint calculations.

debian/control
man/man1/openpgp2ssh.1
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>
 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
 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
 .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
 .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
 .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
 .Pp
 .Nm
 only acts on keys associated with the first primary key
index 92bdc1955c81e0cada4eb80a5177017f5b226575..5cc6cfa087e967e8e8afe49af2839073570fade6 100644 (file)
 
 
 /* FIXME: keyid should be const as well */
 
 
 /* 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_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;
   int subkeyidx;
   int subkeycount;
   int found = 0;
+  unsigned char fingerprint[20];
+  size_t fingerprint_length = sizeof(fingerprint); 
 
   init_datum(&m);
   init_datum(&e);
 
   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;
   }
 
     return 1;
   }
 
-  if ((keyid == NULL) && 
+  if ((keyfpr == NULL) && 
       (subkeycount > 0)) {
       (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;
   }
 
     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) {
     if (ret) {
-      err(0,"Could not get keyid (error: %d)\n", ret);
+      err(0,"Could not get fingerprint (error: %d)\n", ret);
       return 1;
     }
       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");
 
     /* 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++) {
   } 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) {
       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;
       }
        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
        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 */
 }
 
 /* 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;
   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];
 
      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;
   /* 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;
   }
 
     return 1;
   }
 
-  if ((keyid == NULL) && 
+  if ((keyfpr == NULL) && 
       (subkeycount > 0)) {
       (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;
   }
 
     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) {
     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;
     }
       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");
 
     /* 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++) {
   } 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) {
       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;
       }
        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
        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 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;
   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);
   
   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();
 
 
   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) {
   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;
     }
     
       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)
 
     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");
 
       /* 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! */
       
     } 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();
   }
 
   gnutls_global_deinit();
+  free(fingerprint);
   return 0;
 }
   return 0;
 }