From: Daniel Kahn Gillmor Date: Wed, 9 Apr 2008 01:02:35 +0000 (-0400) Subject: working with subkeys: preparing to write a gnutls-based gpg2ssh capable of feeding... X-Git-Tag: monkeysphere_0.1-1~86 X-Git-Url: https://codewiz.org/gitweb?p=monkeysphere.git;a=commitdiff_plain;h=29b46325c50f7b767d1027c817c4e050806f2aa1 working with subkeys: preparing to write a gnutls-based gpg2ssh capable of feeding the monkeysphere. --- diff --git a/.gitignore b/.gitignore index d2cf588..ae9fbd8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *~ *.[ao] monkeysphere +gpg2ssh diff --git a/Makefile b/Makefile index b8545a9..4fb4556 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,9 @@ monkeysphere: main.c gnutls-helpers.o gcc -g -Wall --pedantic -o monkeysphere main.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o +gpg2ssh: gpg2ssh.c gnutls-helpers.o + gcc -g -Wall --pedantic -o gpg2ssh gpg2ssh.c `libgnutls-config --libs --cflags` -lgnutls-extra gnutls-helpers.o + %.o: %.c gcc -g -Wall --pedantic -o $@ -c $< diff --git a/gnutls-helpers.c b/gnutls-helpers.c index f5d37e6..a2f8446 100644 --- a/gnutls-helpers.c +++ b/gnutls-helpers.c @@ -18,6 +18,24 @@ void logfunc(int level, const char* string) { fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string); } +void init_keyid(gnutls_openpgp_keyid_t keyid) { + memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t)); +} + + + +void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid) +{ + static const char hex[16] = "0123456789ABCDEF"; + unsigned int kix = 0, outix = 0; + + while (kix < sizeof(gnutls_openpgp_keyid_t)) { + out[outix] = hex[(keyid[kix] >> 4) & 0x0f]; + out[outix + 1] = hex[keyid[kix] & 0x0f]; + kix++; + outix += 2; + } +} int init_gnutls() { diff --git a/gnutls-helpers.h b/gnutls-helpers.h index 1bf7a1d..c07997f 100644 --- a/gnutls-helpers.h +++ b/gnutls-helpers.h @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -32,6 +33,12 @@ void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src); int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b); void free_datum(gnutls_datum_t* d); +/* keyid manipulations: */ +typedef unsigned char printable_keyid[16]; + +void init_keyid(gnutls_openpgp_keyid_t keyid); +void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid); + /* functions to get data into datum objects: */ /* read the passed-in string, store in a single datum */ @@ -44,3 +51,4 @@ int set_datum_fd(gnutls_datum_t* d, int fd); /* read the file indicated (by na1me) in the fname parameter. store its entire contents in a single datum. */ int set_datum_file(gnutls_datum_t* d, const char* fname); + diff --git a/gpg2ssh.c b/gpg2ssh.c new file mode 100644 index 0000000..87b62a3 --- /dev/null +++ b/gpg2ssh.c @@ -0,0 +1,189 @@ +#include "gnutls-helpers.h" + +#include +#include + +/* for htonl() */ +#include + + +/* + Author: Daniel Kahn Gillmor + Date: Tue, 08 Apr 2008 + License: GPL v3 or later + + monkeysphere public key translator: execute this with an GPG + certificate (public key(s) + userid(s)) on stdin. It currently + only works with RSA keys. + + It will spit out a version of the first key capable of being used + for authentication on stdout. The output format should be suitable + for appending a known_hosts file. + + Requirements: I've only built this so far with GnuTLS v2.3.4 -- + version 2.2.0 does not contain the appropriate pieces. + + */ + +int main(int argc, char* argv[]) { + gnutls_datum_t data; + int ret; + gnutls_openpgp_crt_t openpgp_crt; + gnutls_openpgp_keyid_t keyid; + printable_keyid p_keyid; + unsigned int keyidx; + unsigned int usage, bits; + gnutls_pk_algorithm_t algo; + + gnutls_datum_t m, e, p, q, g, y; + + char output_data[10240]; + size_t ods = sizeof(output_data); + + init_gnutls(); + + init_datum(&data); + + init_datum(&m); + init_datum(&e); + init_datum(&p); + init_datum(&q); + init_datum(&g); + init_datum(&y); + + init_keyid(keyid); + + /* slurp in the private key from stdin */ + if (ret = set_datum_fd(&data, 0), ret) { + err("didn't read file descriptor 0\n"); + return 1; + } + + + if (ret = gnutls_openpgp_crt_init(&openpgp_crt), ret) { + err("Failed to initialize OpenPGP certificate (error: %d)\n", ret); + return 1; + } + + /* format could be either: GNUTLS_OPENPGP_FMT_RAW, + GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW, + otherwise, use BASE64: */ + + if (getenv("MONKEYSPHERE_RAW")) { + err("assuming RAW formatted certificate\n"); + if (ret = gnutls_openpgp_crt_import(openpgp_crt, &data, GNUTLS_OPENPGP_FMT_RAW), ret) { + err("failed to import the OpenPGP certificate in RAW format (error: %d)\n", ret); + return ret; + } + } else { + err("assuming BASE64 formatted certificate\n"); + if (ret = gnutls_openpgp_crt_import (openpgp_crt, &data, GNUTLS_OPENPGP_FMT_BASE64), ret) { + err("failed to import the OpenPGP certificate in BASE64 format (error: %d)\n", ret); + return ret; + } + } + + if (gnutls_openpgp_crt_get_revoked_status(openpgp_crt)) { + err("the primary key was revoked!\n"); + return 1; + } + + if (ret = gnutls_openpgp_crt_get_key_usage(openpgp_crt, &usage), ret) { + err("failed to get the usage flags for the primary key (error: %d)\n", ret); + return ret; + } + if (usage & GNUTLS_KEY_KEY_AGREEMENT) { + err("the primary key can be used for authentication\n"); + + algo = gnutls_openpgp_crt_get_pk_algorithm(openpgp_crt, &bits); + if (algo < 0) { + err("failed to get the algorithm of the OpenPGP public key (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + + err("OpenPGP RSA certificate, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_pk_rsa_raw(openpgp_crt, &m, &e); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA Key, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_pk_dsa_raw(openpgp_crt, &p, &q, &g, &y); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); + return 1; + } + + } else { + err("primary key is only good for: 0x%08x. Trying subkeys...\n", usage); + + if (ret = gnutls_openpgp_crt_get_auth_subkey(openpgp_crt, keyid), ret) { + err("failed to find a subkey capable of authentication (error: %d)\n", ret); + return ret; + } + make_keyid_printable(p_keyid, keyid); + err("found authentication subkey %.16s\n", p_keyid); + + ret = gnutls_openpgp_crt_get_subkey_idx(openpgp_crt, keyid); + if (ret < 0) { + err("could not get the index of subkey %.16s (error: %d)\n", ret); + return ret; + } + keyidx = ret; + + if (gnutls_openpgp_crt_get_subkey_revoked_status(openpgp_crt, keyidx)) { + err("The authentication subkey was revoked!\n"); + return 1; + } + + if (ret = gnutls_openpgp_crt_get_subkey_usage(openpgp_crt, keyidx, &usage), ret) { + err("could not figure out usage of subkey %.16s (error: %d)\n", p_keyid, ret); + return ret; + } + if ((usage & GNUTLS_KEY_KEY_AGREEMENT) == 0) { + err("could not find a subkey with authentication privileges.\n"); + return 1; + } + + /* switch, based on the algorithm in question, to extract the MPI + components: */ + + algo = gnutls_openpgp_crt_get_subkey_pk_algorithm(openpgp_crt, keyidx, &bits); + if (algo < 0) { + err("failed to get the algorithm of the authentication subkey (error: %d)\n", algo); + return algo; + } else if (algo == GNUTLS_PK_RSA) { + + err("OpenPGP RSA subkey, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_subkey_pk_rsa_raw(openpgp_crt, keyidx, &m, &e); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA subkey parameters (error: %d)\n", ret); + return 1; + } + } else if (algo == GNUTLS_PK_DSA) { + err("OpenPGP DSA subkey, with %d bits\n", bits); + ret = gnutls_openpgp_crt_get_subkey_pk_dsa_raw(openpgp_crt, keyidx, &p, &q, &g, &y); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA subkey parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP subkey was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", algo); + return 1; + } + } + + /* now we have algo, and the various MPI data set. Can we export + them cleanly? */ + + + + gnutls_openpgp_crt_deinit(openpgp_crt); + gnutls_global_deinit(); + return 0; +} diff --git a/main.c b/main.c index 638666b..d6bac68 100644 --- a/main.c +++ b/main.c @@ -37,32 +37,13 @@ ssh-rsa AAAAB3NzaC1yc2EAAACBAL2BZCp+ueFAoNCc0P/d457Gl10Trgn7XOn19zIKN7sfkspyVvoM */ - -int main(int argc, char* argv[]) { - gnutls_x509_privkey_t x509_privkey; - gnutls_datum_t data, test, clean; - int ret; - - /* - const char *certfile, *keyfile; - gnutls_certificate_credentials_t pgp_creds; - */ - gnutls_datum_t m, e, d, p, q, u, g, y, x; - - /* gnutls_x509_crt_t crt; */ - +int convert_pgp_to_x509(gnutls_x509_privkey_t* output, gnutls_datum_t* input) { gnutls_openpgp_privkey_t pgp_privkey; + gnutls_datum_t m, e, d, p, q, u, g, y, x; gnutls_pk_algorithm_t pgp_algo; unsigned int pgp_bits; + int ret; - char output_data[10240]; - size_t ods = sizeof(output_data); - - init_gnutls(); - - init_datum(&data); - init_datum(&test); - init_datum(&clean); init_datum(&m); init_datum(&e); init_datum(&d); @@ -73,48 +54,11 @@ int main(int argc, char* argv[]) { init_datum(&y); init_datum(&x); - - if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { - err("Failed to initialize X.509 private key (error: %d)\n", ret); - return 1; - } - if (ret = gnutls_openpgp_privkey_init(&pgp_privkey), ret) { err("Failed to initialized OpenPGP private key (error: %d)\n", ret); return 1; } - /* slurp in the private key from stdin */ - if (ret = set_datum_fd(&data, 0), ret) { - err("didn't read file descriptor 0\n"); - return 1; - } - - /* Or, instead, read in key from a file name: - if (ret = set_datum_file(&data, argv[1]), ret) { - err("didn't read file '%s'\n", argv[1]); - return 1; - } -*/ - - /* treat the passed file as an X.509 private key, and extract its - component values: */ - -/* if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */ -/* err("Failed to import the X.509 key (error: %d)\n", ret); */ -/* return 1; */ -/* } */ -/* gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */ - - /* try to print the PEM-encoded private key: */ -/* ret = gnutls_x509_privkey_export (x509_privkey, */ -/* GNUTLS_X509_FMT_PEM, */ -/* output_data, */ -/* &ods); */ -/* printf("ret: %u; ods: %u;\n", ret, ods); */ -/* if (ret == 0) { */ -/* write(0, output_data, ods); */ -/* } */ /* format could be either: GNUTLS_OPENPGP_FMT_RAW, GNUTLS_OPENPGP_FMT_BASE64; if MONKEYSPHERE_RAW is set, use RAW, @@ -122,11 +66,11 @@ int main(int argc, char* argv[]) { if (getenv("MONKEYSPHERE_RAW")) { err("assuming RAW formatted private keys\n"); - if (ret = gnutls_openpgp_privkey_import(pgp_privkey, &data, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret) + if (ret = gnutls_openpgp_privkey_import(pgp_privkey, input, GNUTLS_OPENPGP_FMT_RAW, NULL, 0), ret) err("failed to import the OpenPGP private key in RAW format (error: %d)\n", ret); } else { err("assuming BASE64 formatted private keys\n"); - if (ret = gnutls_openpgp_privkey_import (pgp_privkey, &data, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret) + if (ret = gnutls_openpgp_privkey_import (pgp_privkey, input, GNUTLS_OPENPGP_FMT_BASE64, NULL, 0), ret) err("failed to import the OpenPGP private key in BASE64 format (error: %d)\n", ret); } @@ -143,7 +87,7 @@ int main(int argc, char* argv[]) { return 1; } - ret = gnutls_x509_privkey_import_rsa_raw (x509_privkey, &m, &e, &d, &p, &q, &u); + ret = gnutls_x509_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); if (GNUTLS_E_SUCCESS != ret) { err ("failed to import RSA key parameters (error: %d)\n", ret); return 1; @@ -156,7 +100,7 @@ int main(int argc, char* argv[]) { return 1; } - ret = gnutls_x509_privkey_import_dsa_raw (x509_privkey, &p, &q, &g, &y, &x); + ret = gnutls_x509_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); if (GNUTLS_E_SUCCESS != ret) { err ("failed to import DSA key parameters (error: %d)\n", ret); return 1; @@ -166,11 +110,151 @@ int main(int argc, char* argv[]) { return 1; } - ret = gnutls_x509_privkey_fix(x509_privkey); + ret = gnutls_x509_privkey_fix(*output); if (ret != 0) { err("failed to fix up the private key in X.509 format (error: %d)\n", ret); return 1; } + + gnutls_openpgp_privkey_deinit(pgp_privkey); + return 0; +} + +int convert_x509_to_pgp(gnutls_openpgp_privkey_t* output, gnutls_datum_t* input) { + gnutls_x509_privkey_t x509_privkey; + gnutls_datum_t m, e, d, p, q, u, g, y, x; + gnutls_pk_algorithm_t x509_algo; + int ret; + + init_datum(&m); + init_datum(&e); + init_datum(&d); + init_datum(&p); + init_datum(&q); + init_datum(&u); + init_datum(&g); + init_datum(&y); + init_datum(&x); + + if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { + err("Failed to initialized X.509 private key (error: %d)\n", ret); + return 1; + } + + + /* format could be either: GNUTLS_X509_FMT_DER, + GNUTLS_X509_FMT_PEM; if MONKEYSPHERE_DER is set, use DER, + otherwise, use PEM: */ + + if (getenv("MONKEYSPHERE_DER")) { + err("assuming DER formatted private keys\n"); + if (ret = gnutls_x509_privkey_import(x509_privkey, input, GNUTLS_X509_FMT_DER), ret) + err("failed to import the X.509 private key in DER format (error: %d)\n", ret); + } else { + err("assuming PEM formatted private keys\n"); + if (ret = gnutls_x509_privkey_import (x509_privkey, input, GNUTLS_X509_FMT_PEM), ret) + err("failed to import the X.509 private key in PEM format (error: %d)\n", ret); + } + + x509_algo = gnutls_x509_privkey_get_pk_algorithm(x509_privkey); + if (x509_algo < 0) { + err("failed to get X.509 key algorithm (error: %d)\n", x509_algo); + return 1; + } + if (x509_algo == GNUTLS_PK_RSA) { + err("X.509 RSA Key\n"); + ret = gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export RSA key parameters (error: %d)\n", ret); + return 1; + } + + /* ret = gnutls_openpgp_privkey_import_rsa_raw (*output, &m, &e, &d, &p, &q, &u); */ + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to import RSA key parameters (error: %d)\n", ret); + return 1; + } + } else if (x509_algo == GNUTLS_PK_DSA) { + err("X.509 DSA Key\n"); + ret = gnutls_x509_privkey_export_dsa_raw(x509_privkey, &p, &q, &g, &y, &x); + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to export DSA key parameters (error: %d)\n", ret); + return 1; + } + + /* ret = gnutls_openpgp_privkey_import_dsa_raw (*output, &p, &q, &g, &y, &x); */ + ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; + if (GNUTLS_E_SUCCESS != ret) { + err ("failed to import DSA key parameters (error: %d)\n", ret); + return 1; + } + } else { + err("OpenPGP Key was not RSA or DSA -- can't deal! (actual algorithm was: %d)\n", x509_algo); + return 1; + } + + gnutls_x509_privkey_deinit(x509_privkey); + return 0; +} + + +int main(int argc, char* argv[]) { + gnutls_datum_t data; + int ret; + gnutls_x509_privkey_t x509_privkey; + + char output_data[10240]; + size_t ods = sizeof(output_data); + + init_gnutls(); + + init_datum(&data); + + /* slurp in the private key from stdin */ + if (ret = set_datum_fd(&data, 0), ret) { + err("didn't read file descriptor 0\n"); + return 1; + } + + + + /* Or, instead, read in key from a file name: + if (ret = set_datum_file(&data, argv[1]), ret) { + err("didn't read file '%s'\n", argv[1]); + return 1; + } +*/ + + /* treat the passed file as an X.509 private key, and extract its + component values: */ + +/* if (ret = gnutls_x509_privkey_import(x509_privkey, &data, GNUTLS_X509_FMT_PEM), ret) { */ +/* err("Failed to import the X.509 key (error: %d)\n", ret); */ +/* return 1; */ +/* } */ +/* gnutls_x509_privkey_export_rsa_raw(x509_privkey, &m, &e, &d, &p, &q, &u); */ + + /* try to print the PEM-encoded private key: */ +/* ret = gnutls_x509_privkey_export (x509_privkey, */ +/* GNUTLS_X509_FMT_PEM, */ +/* output_data, */ +/* &ods); */ +/* printf("ret: %u; ods: %u;\n", ret, ods); */ +/* if (ret == 0) { */ +/* write(0, output_data, ods); */ +/* } */ + + + if (ret = gnutls_x509_privkey_init(&x509_privkey), ret) { + err("Failed to initialize X.509 private key (error: %d)\n", ret); + return 1; + } + + if (ret = convert_pgp_to_x509(&x509_privkey, &data), ret) { + return ret; + } + ret = gnutls_x509_privkey_export (x509_privkey, GNUTLS_X509_FMT_PEM, output_data, @@ -182,7 +266,6 @@ int main(int argc, char* argv[]) { gnutls_x509_privkey_deinit(x509_privkey); - gnutls_openpgp_privkey_deinit(pgp_privkey); gnutls_global_deinit(); return 0; }