Changes to 3.99.12/20100907

+ add a pretty print function mj_pretty(3) to libmj
+ added netpgp_write_sshkey(3) to libnetpgp
+ added pgp2ssh(1)
+ added preliminary support for ElGamal decryption, needed for DSA keys
  as yet untested, unworking, and a WIP
+ add support for using all ssh keys, even those protected by a passphrase,
  for decryption and signing. This rounds off ssh key file support in netpgp.
+ add a single character alias [-S file] for [--sshkeyfile file] to
  netpgpkeys(1) and netpgp(1)

As far as ssh key file support goes, see the following example:

	% cp configure a
	% netpgp -S ~/.ssh/id_rsa.pub -e a
	% netpgp -S ~/.ssh/id_rsa.pub -d a.gpg
	Enter PEM pass phrase:
	% ls -al a a.gpg
	-rwxr-xr-x  1 agc  agc  758398 Sep  7 05:38 a
	-rw-------  1 agc  agc  156886 Sep  7 05:38 a.gpg
	%
This commit is contained in:
agc 2010-09-08 03:21:21 +00:00
parent f3a95c474f
commit 73f34b005f
13 changed files with 256 additions and 26 deletions

View File

@ -6,7 +6,6 @@ agentctl
convert to and from ascii armored sigs
gpgme compat lib
get rid of public key free as part of seckey
return userids from successful verify, and then print id out if required
is get_passphrase_cb needed?
error logging
separate from libcrypto?
@ -97,3 +96,6 @@ hkp to include sigs too
make netpgpkeys work - add, import, commit, update, sign, passphrase
fix ssh fingerprints not matching netpgp
json/yaml output
return userids from successful verify, and then print id out if required
convert between pgp and ssh key formats
PEM ssh keys and passphrases

View File

@ -54,7 +54,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: crypto.c,v 1.27 2010/08/15 07:52:26 agc Exp $");
__RCSID("$NetBSD: crypto.c,v 1.28 2010/09/08 03:21:22 agc Exp $");
#endif
#include <sys/types.h>
@ -143,8 +143,43 @@ __ops_decrypt_decode_mpi(uint8_t *buf,
return n - i;
case OPS_PKA_DSA:
case OPS_PKA_ELGAMAL:
(void) fprintf(stderr, "XXX - no support for DSA/Elgamal yet\n");
return 0;
(void) fprintf(stderr, "XXX - preliminary support for DSA/Elgamal\n");
if (__ops_get_debug_level(__FILE__)) {
hexdump(stderr, "encrypted", encmpibuf, 16);
}
n = __ops_elgamal_private_decrypt(mpibuf, encmpibuf,
(unsigned)(BN_num_bits(encmpi) + 7) / 8,
&seckey->key.elgamal, &seckey->pubkey.key.elgamal);
if (n == -1) {
(void) fprintf(stderr, "ops_elgamal_private_decrypt failure\n");
return -1;
}
if (__ops_get_debug_level(__FILE__)) {
hexdump(stderr, "decrypted", mpibuf, 16);
}
if (n <= 0) {
return -1;
}
/* Decode EME-PKCS1_V1_5 (RFC 2437). */
if (mpibuf[0] != 0 || mpibuf[1] != 2) {
return -1;
}
/* Skip the random bytes. */
for (i = 2; i < n && mpibuf[i]; ++i) {
}
if (i == n || i < 10) {
return -1;
}
/* Skip the zero */
i += 1;
/* this is the unencoded m buf */
if ((unsigned) (n - i) <= buflen) {
(void) memcpy(buf, mpibuf + i, (unsigned)(n - i)); /* XXX - Flexelint */
}
if (__ops_get_debug_level(__FILE__)) {
hexdump(stderr, "decoded m", buf, (size_t)(n - i));
}
return n - i;
default:
(void) fprintf(stderr, "pubkey algorithm wrong\n");
return -1;
@ -216,6 +251,10 @@ write_parsed_cb(const __ops_packet_t *pkt, __ops_cbdata_t *cbinfo)
return __ops_pk_sesskey_cb(pkt, cbinfo);
case OPS_GET_SECKEY:
if (cbinfo->sshseckey) {
*content->get_seckey.seckey = cbinfo->sshseckey;
return OPS_KEEP_MEMORY;
}
return __ops_get_seckey_cb(pkt, cbinfo);
case OPS_GET_PASSPHRASE:
@ -361,6 +400,7 @@ __ops_decrypt_file(__ops_io_t *io,
__ops_keyring_t *pubring,
const unsigned use_armour,
const unsigned allow_overwrite,
const unsigned sshkeys,
void *passfp,
__ops_cbfunc_t *getpassfunc)
{
@ -424,6 +464,7 @@ __ops_decrypt_file(__ops_io_t *io,
parse->cbinfo.passfp = passfp;
parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
parse->cbinfo.cryptinfo.pubring = pubring;
parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
/* Set up armour/passphrase options */
if (use_armour) {
@ -456,6 +497,7 @@ __ops_decrypt_buf(__ops_io_t *io,
__ops_keyring_t *secring,
__ops_keyring_t *pubring,
const unsigned use_armour,
const unsigned sshkeys,
void *passfp,
__ops_cbfunc_t *getpassfunc)
{
@ -487,6 +529,7 @@ __ops_decrypt_buf(__ops_io_t *io,
parse->cbinfo.cryptinfo.pubring = pubring;
parse->cbinfo.passfp = passfp;
parse->cbinfo.cryptinfo.getpassphrase = getpassfunc;
parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL;
/* Set up armour/passphrase options */
if (use_armour) {

View File

@ -145,6 +145,9 @@ int __ops_rsa_private_encrypt(uint8_t *, const uint8_t *, size_t,
int __ops_rsa_private_decrypt(uint8_t *, const uint8_t *, size_t,
const __ops_rsa_seckey_t *, const __ops_rsa_pubkey_t *);
int __ops_elgamal_private_decrypt(uint8_t *, const uint8_t *, size_t,
const __ops_elgamal_seckey_t *, const __ops_elgamal_pubkey_t *);
unsigned __ops_block_size(__ops_symm_alg_t);
unsigned __ops_key_size(__ops_symm_alg_t);
@ -189,6 +192,7 @@ unsigned __ops_decrypt_file(__ops_io_t *,
__ops_keyring_t *,
const unsigned,
const unsigned,
const unsigned,
void *,
__ops_cbfunc_t *);
@ -205,6 +209,7 @@ __ops_decrypt_buf(__ops_io_t *,
__ops_keyring_t *,
__ops_keyring_t *,
const unsigned,
const unsigned,
void *,
__ops_cbfunc_t *);
@ -256,6 +261,7 @@ struct __ops_cbdata_t {
void *passfp; /* fp for passphrase input */
__ops_cryptinfo_t cryptinfo; /* used when decrypting */
__ops_printstate_t printstate; /* used to keep state when printing */
__ops_seckey_t *sshseckey; /* secret key for ssh */
};
/** __ops_hashtype_t */

View File

@ -34,7 +34,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: netpgp.c,v 1.74 2010/09/06 18:19:38 agc Exp $");
__RCSID("$NetBSD: netpgp.c,v 1.75 2010/09/08 03:21:22 agc Exp $");
#endif
#include <sys/types.h>
@ -264,6 +264,12 @@ readsshkeys(netpgp_t *netpgp, char *homedir, const char *needseckey)
if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
(void) snprintf(f, sizeof(f), "%s/id_rsa.pub", homedir);
filename = f;
} else {
/* got ssh keys, check for pub file name */
if (strcmp(&filename[strlen(filename) - 4], ".pub") != 0) {
(void) fprintf(stderr, "readsshkeys: bad pubkey filename '%s'\n", filename);
return 0;
}
}
if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
(void) fprintf(stderr, "readsshkeys: bad alloc\n");
@ -1185,6 +1191,7 @@ netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
const unsigned overwrite = 1;
__ops_io_t *io;
unsigned realarmor;
unsigned sshkeys;
__OPS_USED(armored);
io = netpgp->io;
@ -1194,9 +1201,10 @@ netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
return 0;
}
realarmor = isarmoured(io, f, NULL, ARMOR_HEAD);
sshkeys = (unsigned)(netpgp_getvar(netpgp, "ssh keys") != NULL);
return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
netpgp->pubring,
realarmor, overwrite,
realarmor, overwrite, sshkeys,
netpgp->passfp, get_passphrase_cb);
}
@ -1491,6 +1499,7 @@ netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
__ops_memory_t *mem;
__ops_io_t *io;
unsigned realarmour;
unsigned sshkeys;
size_t m;
__OPS_USED(armored);
@ -1501,9 +1510,11 @@ netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
return 0;
}
realarmour = isarmoured(io, NULL, input, ARMOR_HEAD);
sshkeys = (unsigned)(netpgp_getvar(netpgp, "ssh keys") != NULL);
mem = __ops_decrypt_buf(netpgp->io, input, insize, netpgp->secring,
netpgp->pubring,
realarmour, netpgp->passfp,
realarmour, sshkeys,
netpgp->passfp,
get_passphrase_cb);
m = MIN(__ops_mem_len(mem), outsize);
(void) memcpy(out, __ops_mem_data(mem), m);

View File

@ -57,7 +57,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: openssl_crypto.c,v 1.28 2010/09/07 00:25:37 agc Exp $");
__RCSID("$NetBSD: openssl_crypto.c,v 1.29 2010/09/08 03:21:22 agc Exp $");
#endif
#ifdef HAVE_OPENSSL_DSA_H
@ -77,7 +77,10 @@ __RCSID("$NetBSD: openssl_crypto.c,v 1.28 2010/09/07 00:25:37 agc Exp $");
#include <stdlib.h>
#include <string.h>
/* Hash size for secret key check */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "crypto.h"
#include "keyring.h"
@ -834,10 +837,13 @@ int
openssl_read_pem_seckey(const char *f, __ops_key_t *key, const char *type, int verbose)
{
FILE *fp;
char prompt[BUFSIZ];
char *pass;
DSA *dsa;
RSA *rsa;
int ok;
OpenSSL_add_all_algorithms();
if ((fp = fopen(f, "r")) == NULL) {
if (verbose) {
(void) fprintf(stderr, "can't open '%s'\n", f);
@ -847,13 +853,16 @@ openssl_read_pem_seckey(const char *f, __ops_key_t *key, const char *type, int v
ok = 1;
if (strcmp(type, "ssh-rsa") == 0) {
if ((rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)) == NULL) {
ok = 0;
} else {
key->key.seckey.key.rsa.d = rsa->d;
key->key.seckey.key.rsa.p = rsa->p;
key->key.seckey.key.rsa.q = rsa->q;
key->key.seckey.key.rsa.d = rsa->d;
(void) snprintf(prompt, sizeof(prompt), "netpgp PEM %s passphrase: ", f);
do {
pass = getpass(prompt);
rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL, pass);
} while (rsa == NULL);
}
key->key.seckey.key.rsa.d = rsa->d;
key->key.seckey.key.rsa.p = rsa->p;
key->key.seckey.key.rsa.q = rsa->q;
key->key.seckey.key.rsa.d = rsa->d;
} else if (strcmp(type, "ssh-dss") == 0) {
if ((dsa = PEM_read_DSAPrivateKey(fp, NULL, NULL, NULL)) == NULL) {
ok = 0;
@ -866,3 +875,77 @@ openssl_read_pem_seckey(const char *f, __ops_key_t *key, const char *type, int v
(void) fclose(fp);
return ok;
}
int
__ops_elgamal_private_decrypt(uint8_t *out,
const uint8_t *in,
size_t length,
const __ops_elgamal_seckey_t *seckey,
const __ops_elgamal_pubkey_t *pubkey)
{
BIGNUM *bndiv;
BIGNUM *c1x;
BN_CTX *tmp;
BIGNUM *c1;
BIGNUM *c2;
BIGNUM *p;
BIGNUM *x;
BIGNUM *m;
int ret = 0;
/* split in byutes into c1 and c2 */
c1 = BN_bin2bn(in, (int)(length / 2), NULL);
c2 = BN_bin2bn(&in[length / 2], (int)(length / 2), NULL);
/* other bits */
p = pubkey->p;
x = seckey->x;
c1x = BN_new();
bndiv = BN_new();
m = BN_new();
tmp = BN_CTX_new();
if (!c1 || !c2 || !p || !x || !c1x || !bndiv || !m || !tmp) {
goto done;
}
/*
* m = c2 / (c1^x)
*/
if (!BN_mod_exp(c1x, c1, x, p, tmp)) {
goto done;
}
if (!BN_mod_inverse(bndiv, c1x, p, tmp)) {
goto done;
}
if (!BN_mod_mul(m, c2, bndiv, p, tmp)) {
goto done;
}
/* result */
if (BN_bn2bin(m, out) > 0) {
ret = 1;
}
done:
if (tmp) {
BN_CTX_free(tmp);
}
if (m) {
BN_clear_free(m);
}
if (bndiv) {
BN_clear_free(bndiv);
}
if (c1x) {
BN_clear_free(c1x);
}
if (x) {
BN_clear_free(x);
}
if (p) {
BN_clear_free(p);
}
if (c1) {
BN_clear_free(c1);
}
if (c2) {
BN_clear_free(c2);
}
return ret;
}

View File

@ -58,7 +58,7 @@
#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: packet-parse.c,v 1.41 2010/08/15 16:36:24 agc Exp $");
__RCSID("$NetBSD: packet-parse.c,v 1.42 2010/09/08 03:21:22 agc Exp $");
#endif
#ifdef HAVE_OPENSSL_CAST_H
@ -2550,7 +2550,6 @@ parse_seckey(__ops_region_t *region, __ops_stream_t *stream)
break;
case OPS_PKA_ELGAMAL:
printf("elgamal reading\n");
if (!limread_mpi(&pkt.u.seckey.key.elgamal.x, region, stream)) {
ret = 0;
}
@ -2652,7 +2651,6 @@ parse_pk_sesskey(__ops_region_t *region,
unsigned k;
BIGNUM *enc_m;
int n;
/* Can't rely on it being CAST5 */
/* \todo FIXME RW */
/* const size_t sz_unencoded_m_buf=CAST_KEY_LENGTH+1+2; */

View File

@ -242,6 +242,13 @@ __ops_ssh2pubkey(__ops_io_t *io, const char *f, __ops_key_t *key, __ops_hash_alg
bufgap_seek(&bg, 1, BGFromHere, BGByte);
off = bufgap_tell(&bg, BGFromBOF, BGByte);
if (bufgap_size(&bg, BGByte) - off < 10) {
(void) fprintf(stderr, "bad key file '%s'\n", f);
(void) free(buf);
bufgap_close(&bg);
return 0;
}
/* convert from base64 to binary */
cc = bufgap_getbin(&bg, buf, (size_t)st.st_size);
if ((space = strchr(buf, ' ')) != NULL) {

View File

@ -58,7 +58,7 @@
#endif
/* development versions have .99 suffix */
#define NETPGP_BASE_VERSION "3.99.11"
#define NETPGP_BASE_VERSION "3.99.12"
#define NETPGP_VERSION_CAT(a, b) "NetPGP portable " a "/[" b "]"
#define NETPGP_VERSION_STRING \

View File

@ -85,6 +85,10 @@ JSON object output functions:
.Fo mj_string_size
.Fa "mj_t *atom"
.Fc
.Ft int
.Fo mj_pretty
.Fa "mj_t *atom" "void *stream" "unsigned depth" "const char *trailer"
.Fc
.Sh DESCRIPTION
.Nm
is a small library interface to allow JSON text to be created and parsed.
@ -144,12 +148,28 @@ using
and the JSON serialised text is copied into it.
This memory can later be de-allocated using
.Xr free 3 .
For formatted output to a
.Dv FILE *
stream, the
.Fn mj_pretty
function is used.
The calling interface gives the ability to indent the
output to a given
.Fa depth
and for the formatted output to be followed by a
.Fa trailer
string, which is usually NULL for external calls,
but can be any valid string.
Output is sent to the
.Fa stream
file stream.
.Pp
The
.Fa type
argument given to the
.Fn mj_create ,
.Fn mj_append and
.Fn mj_append ,
and
.Fn mj_append_field
functions is taken from a list of
.Dq false
@ -171,9 +191,6 @@ Within a JSON object, the key values can be iterated over using an integer
index to access the individual
JSON objects.
The index can also be found using the
.Fn mj_object_index
function, and the object using
the
.Fn mj_object_find
function.
.Pp

View File

@ -152,6 +152,20 @@ gettok(const char *s, int *from, int *to, int *tok)
return *tok;
}
/* minor function used to indent a JSON field */
static void
indent(FILE *fp, unsigned depth, const char *trailer)
{
unsigned i;
for (i = 0 ; i < depth ; i++) {
(void) fprintf(fp, " ");
}
if (trailer) {
(void) fprintf(fp, "%s", trailer);
}
}
/***************************************************************************/
/* return the number of entries in the array */
@ -479,6 +493,7 @@ mj_append_field(mj_t *atom, const char *name, const char *type, ...)
return 1;
}
/* make sure a JSON object is politically correct */
int
mj_lint(mj_t *obj)
{
@ -519,3 +534,42 @@ mj_lint(mj_t *obj)
return 0;
}
}
/* pretty-print a JSON struct - can be called recursively */
int
mj_pretty(mj_t *mj, void *vp, unsigned depth, const char *trailer)
{
unsigned i;
FILE *fp;
fp = (FILE *)vp;
switch(mj->type) {
case MJ_NUMBER:
case MJ_TRUE:
case MJ_FALSE:
case MJ_NULL:
indent(fp, depth, mj->value.s);
break;
case MJ_STRING:
indent(fp, depth, NULL);
(void) fprintf(fp, "\"%s\"", mj->value.s);
break;
case MJ_ARRAY:
indent(fp, depth, "[\n");
for (i = 0 ; i < mj->c ; i++) {
mj_pretty(&mj->value.v[i], fp, depth + 1, (i < mj->c - 1) ? ",\n" : "\n");
}
indent(fp, depth, "]");
break;
case MJ_OBJECT:
indent(fp, depth, "{\n");
for (i = 0 ; i < mj->c ; i += 2) {
mj_pretty(&mj->value.v[i], fp, depth + 1, " : ");
mj_pretty(&mj->value.v[i + 1], fp, 0, (i < mj->c - 2) ? ",\n" : "\n");
}
indent(fp, depth, "}");
break;
}
indent(fp, 0, trailer);
return 1;
}

View File

@ -64,5 +64,6 @@ int mj_lint(mj_t *);
int mj_snprint(char *, size_t, mj_t *);
int mj_asprint(char **, mj_t *);
int mj_string_size(mj_t *);
int mj_pretty(mj_t *, void *, unsigned, const char *);
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: netpgp.c,v 1.14 2010/09/01 17:25:57 agc Exp $ */
/* $NetBSD: netpgp.c,v 1.15 2010/09/08 03:21:22 agc Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@ -527,7 +527,7 @@ main(int argc, char **argv)
netpgp_setvar(&netpgp, "max mem alloc", "4194304");
homeset = 0;
optindex = 0;
while ((ch = getopt_long(argc, argv, "Vdeo:sv", options, &optindex)) != -1) {
while ((ch = getopt_long(argc, argv, "S:Vdeo:sv", options, &optindex)) != -1) {
if (ch >= ENCRYPT) {
/* getopt_long returns 0 for long options */
if (!setoption(&netpgp, &p, options[optindex].val, optarg, &homeset)) {
@ -535,6 +535,10 @@ main(int argc, char **argv)
}
} else {
switch (ch) {
case 'S':
netpgp_setvar(&netpgp, "ssh keys", "1");
netpgp_setvar(&netpgp, "sshkeyfile", optarg);
break;
case 'V':
printf(
"%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",

View File

@ -387,7 +387,7 @@ main(int argc, char **argv)
netpgp_setvar(&netpgp, "res", "<stdout>");
netpgp_setvar(&netpgp, "hash", DEFAULT_HASH_ALG);
optindex = 0;
while ((ch = getopt_long(argc, argv, "Vglo:s", options, &optindex)) != -1) {
while ((ch = getopt_long(argc, argv, "S:Vglo:s", options, &optindex)) != -1) {
if (ch >= LIST_KEYS) {
/* getopt_long returns 0 for long options */
if (!setoption(&netpgp, &p, options[optindex].val, optarg, &homeset)) {
@ -395,6 +395,10 @@ main(int argc, char **argv)
}
} else {
switch (ch) {
case 'S':
netpgp_setvar(&netpgp, "ssh keys", "1");
netpgp_setvar(&netpgp, "sshkeyfile", optarg);
break;
case 'V':
printf(
"%s\nAll bug reports, praise and chocolate, please, to:\n%s\n",