Secret-Sharing: changes to existing code
Phil Sutter
sutter at informatik.hs-furtwangen.de
Thu Jul 31 21:38:24 CEST 2008
Hi!
After solving my "decrypt shares to internal buffer" issue my proof of
concept code now provides all the functionalities I wanted to be
available before considering it the right way to go. So with my patched
version of gpg I can:
* setup an existing secret key for being shared (N is the threshold)
| gpg --ss-setup <N> <identifier>
* generate encrypted shares for an existing session
| gpg -r <identifier> -o sharefile --gen-share <identifier>
* list information about a share file
| gpg --list-packets sharefile
* list information about "open" sharing/recombining sessions
| gpg --ss-info <identifier (optional)>
* add a share for recombination
| gpg --ss-add-share sharefile
* clear sharing/recombining metadata
| gpg --ss-clear <identifier (optional)>
for now, there is no command for explicitly solving a recombining
session, as this is done each time after adding a share. The combiner is
able to detect whether the secret is already found or not. If so, the
secret data is being sent to gpg and imported to the secret keyring.
There are more features I could think of:
* a way for participants to store shares, and a command to prepare a
share for sending it to the combiner (i.e. de- and encrypting it)
* finer grained control about what data to clear with --ss-clear
(including removal of the secret key itself from the keyring)
* maybe some way to automate recombining shares via network (perhaps a
task for gpg-server?)
* maybe usage of these key-stubs and minimising the data being shared to
only the secret key params
but as I have to finish my diploma thesis first, from now on I will
concentrate on writing. Meanwhile I start sending in code for being
reviewed. The attachment contains only the changes to the existing files
to keep it simple for now. The rest will follow in chunks after I have
fixed all your concerns with this one.
Greetings, Phil
PS: something in advance: it may well be possible that I messed up
indentation in some cases, as it actually is not very consistent
throughout the existing code.
-------------- next part --------------
Index: configure.ac
===================================================================
--- configure.ac (revision 4803)
+++ configure.ac (working copy)
@@ -132,6 +132,13 @@ AM_CONDITIONAL(GNUPG_SCDAEMON_PGM, test -n "$GNUPG
show_gnupg_scdaemon_pgm="(default)"
test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM"
+AC_ARG_WITH(ssd-pgm,
+ [ --with-ssd-pgm=PATH Use PATH as the default for ssd)],
+ GNUPG_SSD_PGM="$withval", GNUPG_SSD_PGM="" )
+AC_SUBST(GNUPG_SSD_PGM)
+AM_CONDITIONAL(GNUPG_SSD_PGM, test -n "$GNUPG_SSD_PGM")
+show_gnupg_ssd_pgm="(default)"
+test -n "$GNUPG_SSD_PGM" && show_gnupg_ssd_pgm="$GNUPG_SSD_PGM"
AC_ARG_WITH(dirmngr-pgm,
[ --with-dirmngr-pgm=PATH Use PATH as the default for the dirmngr)],
@@ -158,6 +165,14 @@ AC_ARG_ENABLE(agent-only,
AC_HELP_STRING([--enable-agent-only],[build only the gpg-agent]),
build_agent_only=$enableval)
+# Enable secret-sharing
+AC_MSG_CHECKING([whether to enable secret-sharing])
+AC_ARG_ENABLE(secret-sharing,
+ AC_HELP_STRING([--enable-secret-sharing],
+ [Enable secret-sharing and build ssd]),
+ secret_sharing=$enableval, secret_sharing=no)
+AC_MSG_RESULT($secret_sharing)
+
# SELinux support includes tracking of sensitive files to avoid
# leaking their contents through processing these files by gpg itself
AC_MSG_CHECKING([whether SELinux support is requested])
@@ -971,6 +986,12 @@ if test "$selinux_support" = yes ; then
AC_DEFINE(ENABLE_SELINUX_HACKS,1,[Define to enable SELinux support])
fi
+#
+# support for Secret-Sharing
+#
+if test "$secret_sharing" = yes ; then
+ AC_DEFINE(ENABLE_SECRET_SHARING,1,[Define to enable Secret-Sharing])
+fi
#
# Checks for header files.
@@ -1332,11 +1353,16 @@ if test "$build_scdaemon" = "yes"; then
fi
fi
+if test "$enable_secret_sharing" = "yes" ; then
+ build_ssd=yes
+fi
+
if test "$build_agent_only" = "yes" ; then
build_gpg=no
build_gpgsm=no
build_scdaemon=no
+ build_ssd=no
build_tools=no
build_doc=no
fi
@@ -1346,6 +1372,7 @@ AM_CONDITIONAL(BUILD_GPG, test "$build_gpg" = "y
AM_CONDITIONAL(BUILD_GPGSM, test "$build_gpgsm" = "yes")
AM_CONDITIONAL(BUILD_AGENT, test "$build_agent" = "yes")
AM_CONDITIONAL(BUILD_SCDAEMON, test "$build_scdaemon" = "yes")
+AM_CONDITIONAL(BUILD_SSD, test "$build_ssd" = "yes")
AM_CONDITIONAL(BUILD_TOOLS, test "$build_tools" = "yes")
AM_CONDITIONAL(BUILD_DOC, test "$build_doc" = "yes")
AM_CONDITIONAL(BUILD_SYMCRYPTRUN, test "$build_symcryptrun" = "yes")
@@ -1436,6 +1463,7 @@ g10/Makefile
sm/Makefile
agent/Makefile
scd/Makefile
+ssd/Makefile
keyserver/Makefile
keyserver/gpg2keys_mailto
keyserver/gpg2keys_test
@@ -1458,11 +1486,13 @@ echo "
S/MIME: $build_gpgsm
Agent: $build_agent $build_agent_threaded
Smartcard: $build_scdaemon $build_scdaemon_extra
+ Secret-Sharing: $enable_secret_sharing
Protect tool: $show_gnupg_protect_tool_pgm
Default agent: $show_gnupg_agent_pgm
Default pinentry: $show_gnupg_pinentry_pgm
Default scdaemon: $show_gnupg_scdaemon_pgm
+ Default ssd: $show_gnupg_ssd_pgm
Default dirmngr: $show_gnupg_dirmngr_pgm
"
if test x"$use_regex" != xyes ; then
Index: g10/encr-data.c
===================================================================
--- g10/encr-data.c (revision 4803)
+++ g10/encr-data.c (working copy)
@@ -72,7 +72,8 @@ release_dfx_context (decode_filter_ctx_t dfx)
* Decrypt the data, specified by ED with the key DEK.
*/
int
-decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
+decrypt_data( void *procctx, PKT_encrypted *ed,
+ DEK *dek, int (*callback)(iobuf_t, void *) )
{
decode_filter_ctx_t dfx;
byte *p;
@@ -187,7 +188,10 @@ int
else
iobuf_push_filter ( ed->buf, decode_filter, dfx );
- proc_packets ( procctx, ed->buf );
+ if ( callback )
+ callback ( ed->buf, procctx );
+ else
+ proc_packets ( procctx, ed->buf );
ed->buf = NULL;
if ( ed->mdc_method && dfx->eof_seen == 2 )
rc = gpg_error (GPG_ERR_INV_PACKET);
Index: g10/gpgv.c
===================================================================
--- g10/gpgv.c (revision 4803)
+++ g10/gpgv.c (working copy)
@@ -303,7 +303,8 @@ get_override_session_key( DEK *dek, const char *st
}
/* Stub: */
int
-decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
+decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek,
+ int (*callback)(iobuf_t, void *) )
{
return G10ERR_GENERAL;
}
@@ -383,3 +384,7 @@ void destroy_dotlock (DOTLOCK h) {}
int make_dotlock( DOTLOCK h, long timeout ) { return 0;}
int release_dotlock( DOTLOCK h ) {return 0;}
void remove_lockfiles(void) {}
+
+/* Stubs to avoid linking to call-ssd.c */
+int ssd_put_share(u32 *keyid, void *data, u32 len) { return 0; }
+
Index: g10/mainproc.c
===================================================================
--- g10/mainproc.c (revision 4803)
+++ g10/mainproc.c (working copy)
@@ -63,6 +63,7 @@ struct mainproc_context
md_filter_context_t mfx;
int sigs_only; /* Process only signatures and reject all other stuff. */
int encrypt_only; /* Process only encryption messages. */
+ int shares_only; /* Process only gpg shares (ignored else) */
/* Name of the file with the complete signature or the file with the
detached signature. This is currently only used to deduce the
@@ -480,7 +481,13 @@ print_pkenc_list( struct kidlist_item *list, int f
}
}
+static int
+proc_gpg_share_cb( IOBUF a, void *info )
+{
+ return proc_gpg_share_packets( info, a );
+}
+
static void
proc_encrypted( CTX c, PACKET *pkt )
{
@@ -555,8 +562,11 @@ proc_encrypted( CTX c, PACKET *pkt )
}
else if( !c->dek )
result = G10ERR_NO_SECKEY;
- if( !result )
- result = decrypt_data( c, pkt->pkt.encrypted, c->dek );
+ if( !result && c->shares_only )
+ result = decrypt_data( c, pkt->pkt.encrypted,
+ c->dek, proc_gpg_share_cb);
+ else if ( !result )
+ result = decrypt_data( c, pkt->pkt.encrypted, c->dek, NULL );
if( result == -1 )
;
@@ -767,6 +777,8 @@ proc_compressed( CTX c, PACKET *pkt )
rc = handle_compressed( c, zd, proc_compressed_cb, c );
else if( c->encrypt_only )
rc = handle_compressed( c, zd, proc_encrypt_cb, c );
+ else if( c->shares_only )
+ rc = handle_compressed( c, zd, proc_gpg_share_cb, c );
else
rc = handle_compressed( c, zd, NULL, NULL );
if( rc )
@@ -775,6 +787,16 @@ proc_compressed( CTX c, PACKET *pkt )
c->last_was_session_key = 0;
}
+static void
+proc_gpg_share( CTX c, PACKET *pkt)
+{
+ PKT_gpg_share *sh = pkt->pkt.gpg_share;
+ printf("proc_gpg_share(): handling packet of type %d\n", pkt->pkttype);
+
+ handle_share(c, sh);
+ free_packet(pkt);
+}
+
/****************
* check the signature
* Returns: 0 = valid signature or an error code
@@ -1256,6 +1278,18 @@ proc_encryption_packets( void *anchor, IOBUF a )
return rc;
}
+int
+proc_gpg_share_packets( void *anchor, IOBUF a )
+{
+ CTX c = xmalloc_clear( sizeof *c );
+ int rc;
+
+ c->anchor = anchor;
+ c->shares_only = 1;
+ rc = do_proc_packets( c, a );
+ xfree( c );
+ return rc;
+}
int
do_proc_packets( CTX c, IOBUF a )
@@ -1329,6 +1363,27 @@ do_proc_packets( CTX c, IOBUF a )
default: newpkt = 0; break;
}
}
+ else if( c->shares_only ) {
+ switch( pkt->pkttype ) {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_USER_ID:
+ case PKT_PLAINTEXT:
+ case PKT_SIGNATURE:
+ case PKT_SYMKEY_ENC:
+ case PKT_ONEPASS_SIG:
+ case PKT_GPG_CONTROL:
+ write_status_text( STATUS_UNEXPECTED, "0" );
+ rc = G10ERR_UNEXPECTED;
+ goto leave;
+ case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
+ case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
+ case PKT_GPG_SHARE: proc_gpg_share(c, pkt); break;
+ default: newpkt = 0; break;
+ }
+ }
else {
switch( pkt->pkttype ) {
case PKT_PUBLIC_KEY:
Index: g10/build-packet.c
===================================================================
--- g10/build-packet.c (revision 4803)
+++ g10/build-packet.c (working copy)
@@ -41,6 +41,7 @@ static int do_symkey_enc( IOBUF out, int ctb, PKT_
static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc );
static u32 calc_plaintext( PKT_plaintext *pt );
static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
+static int do_gpg_share( IOBUF out, int ctb, PKT_gpg_share *sh );
static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
@@ -122,6 +123,9 @@ build_packet( IOBUF out, PACKET *pkt )
case PKT_PLAINTEXT:
rc = do_plaintext( out, ctb, pkt->pkt.plaintext );
break;
+ case PKT_GPG_SHARE:
+ rc = do_gpg_share( out, ctb, pkt->pkt.gpg_share );
+ break;
case PKT_ENCRYPTED:
rc = do_encrypted( out, ctb, pkt->pkt.encrypted );
break;
@@ -549,6 +553,17 @@ do_plaintext( IOBUF out, int ctb, PKT_plaintext *p
return rc;
}
+static int
+do_gpg_share( IOBUF out, int ctb, PKT_gpg_share *sh )
+{
+ int rc;
+
+ write_header(out, ctb, calc_gpg_share_len(sh));
+ if ((rc = write_gpg_share_pkt(out, sh)))
+ log_error("do_gpg_share(): could not write share packet: %s\n",
+ gpg_strerror(rc));
+ return rc;
+}
static int
Index: g10/parse-packet.c
===================================================================
--- g10/parse-packet.c (revision 4803)
+++ g10/parse-packet.c (working copy)
@@ -77,6 +77,8 @@ static int parse_mdc( IOBUF inp, int pkttype, uns
PACKET *packet, int new_ctb);
static int parse_gpg_control( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *packet, int partial );
+static int parse_gpg_share( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int partial );
static unsigned short
read_16(IOBUF inp)
@@ -578,6 +580,9 @@ parse( IOBUF inp, PACKET *pkt, int onlykeypkts, of
case PKT_GPG_CONTROL:
rc = parse_gpg_control(inp, pkttype, pktlen, pkt, partial );
break;
+ case PKT_GPG_SHARE:
+ rc = parse_gpg_share( inp, pkttype, pktlen, pkt, partial );
+ break;
case PKT_MARKER:
rc = parse_marker(inp,pkttype,pktlen);
break;
@@ -2425,6 +2430,25 @@ parse_mdc( IOBUF inp, int pkttype, unsigned long p
return rc;
}
+static int
+parse_gpg_share(iobuf_t inp, int pkttype,
+ unsigned long pktlen, PACKET *packet, int partial)
+{
+ PKT_gpg_share *sh;
+
+ packet->pkt.gpg_share = read_gpg_share_pkt(inp, pktlen);
+ if ((sh = packet->pkt.gpg_share) == NULL) {
+ log_error("reading share packet failed\n");
+ return gpg_error(GPG_ERR_INV_PACKET);
+ }
+
+ if (list_mode) {
+ fprintf(listfp, ":gpg share:\n\tkeyid: %08x%08x\n\tshare "
+ "number: %u\n\tlength: %u\n", sh->keyid[0],
+ sh->keyid[1], sh->id, sh->len);
+ }
+ return 0;
+}
/*
* This packet is internally generated by PGG (by armor.c) to
Index: g10/packet.h
===================================================================
--- g10/packet.h (revision 4803)
+++ g10/packet.h (working copy)
@@ -318,6 +318,13 @@ typedef struct {
} PKT_plaintext;
typedef struct {
+ u32 len; /* length of share data */
+ u32 keyid[2]; /* ID of shared key */
+ unsigned char id; /* session internal ID of share */
+ char data[1]; /* share data */
+} PKT_gpg_share;
+
+typedef struct {
int control;
size_t datalen;
char data[1];
@@ -341,6 +348,7 @@ struct packet_struct {
PKT_mdc *mdc; /* PKT_MDC */
PKT_ring_trust *ring_trust; /* PKT_RING_TRUST */
PKT_plaintext *plaintext; /* PKT_PLAINTEXT */
+ PKT_gpg_share *gpg_share; /* PKT_GPG_SHARE */
PKT_gpg_control *gpg_control; /* PKT_GPG_CONTROL */
} pkt;
};
@@ -372,6 +380,7 @@ int proc_signature_packets( void *ctx, iobuf_t a,
strlist_t signedfiles, const char *sigfile );
int proc_signature_packets_by_fd ( void *anchor, IOBUF a, int signed_data_fd );
int proc_encryption_packets( void *ctx, iobuf_t a );
+int proc_gpg_share_packets( void *ctx, iobuf_t a );
int list_packets( iobuf_t a );
/*-- parse-packet.c --*/
@@ -484,7 +493,8 @@ int handle_compressed( void *ctx, PKT_compressed *
int (*callback)(iobuf_t, void *), void *passthru );
/*-- encr-data.c --*/
-int decrypt_data( void *ctx, PKT_encrypted *ed, DEK *dek );
+int decrypt_data( void *ctx, PKT_encrypted *ed, DEK *dek,
+ int (*callback)(iobuf_t, void *));
/*-- plaintext.c --*/
int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx,
@@ -511,4 +521,10 @@ int update_keysig_packet( PKT_signature **ret_sig,
/*-- keygen.c --*/
PKT_user_id *generate_user_id(void);
+/*-- share.c --*/
+u32 calc_gpg_share_len(PKT_gpg_share *);
+void handle_share(void *, PKT_gpg_share *);
+int write_gpg_share_pkt(iobuf_t, PKT_gpg_share *);
+PKT_gpg_share *read_gpg_share_pkt(iobuf_t, unsigned long);
+
#endif /*G10_PACKET_H*/
Index: g10/gpg.c
===================================================================
--- g10/gpg.c (revision 4803)
+++ g10/gpg.c (working copy)
@@ -53,6 +53,7 @@
#include "keyserver-internal.h"
#include "exec.h"
#include "gc-opt-flags.h"
+#include "secret-sharing.h"
#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
#define MY_O_BINARY O_BINARY
@@ -147,6 +148,11 @@ enum cmd_and_opt_values
aCardEdit,
aChangePIN,
aServer,
+ aSSSetup,
+ aSSInfo,
+ aSSAddShare,
+ aSSGenShare,
+ aSSClear,
oTextmode,
oNoTextmode,
@@ -431,6 +437,11 @@ static ARGPARSE_OPTS opts[] = {
{ aPrimegen, "gen-prime" , 256, "@" },
{ aGenRandom, "gen-random", 256, "@" },
{ aServer, "server", 256, N_("run in server mode")},
+ { aSSSetup, "ss-setup", 0, N_("setup a new secret-sharing")},
+ { aSSInfo, "ss-info", 0, N_("retrieve info about secret-sharing")},
+ { aSSAddShare, "ss-add-share", 0, N_("add a share to the combiner")},
+ { aSSGenShare, "ss-gen-share", 0, N_("generate a new share")},
+ { aSSClear, "ss-clear", 0, N_("clear secret-sharing metadata for a key")},
{ 301, NULL, 0, N_("@\nOptions:\n ") },
@@ -2158,6 +2169,21 @@ main (int argc, char **argv)
set_cmd (&cmd, pargs.r_opt);
opt.batch = 1;
break;
+ case aSSSetup:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+ case aSSInfo:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+ case aSSAddShare:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+ case aSSGenShare:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+ case aSSClear:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
case oArmor: opt.armor = 1; opt.no_armor=0; break;
case oOutput: opt.outfile = pargs.r.ret_str; break;
@@ -3937,6 +3963,47 @@ main (int argc, char **argv)
xfree(str);
}
break;
+ case aSSSetup:
+ if (argc != 2)
+ wrong_args("--ss-setup Threshold KeyID");
+ {
+ int thold;
+ char *endptr;
+ thold = strtol(*argv, &endptr, 10);
+ if (strlen(endptr) || thold < 2)
+ log_error("Threshold needs to be >= 2\n");
+ else
+ ss_setup(thold, *(argv + 1));
+ }
+ //printf("would now setup Secret Sharing for %s\n", *argv);
+ break;
+ case aSSInfo:
+ if (argc > 1)
+ wrong_args("--ss-info [KeyID]");
+ if (argc)
+ ss_info(*argv);
+ else
+ ss_info(NULL);
+ break;
+ case aSSAddShare:
+ if (!argc)
+ wrong_args("--ss-add-share filename [filename ...]");
+ {
+ int i;
+ for (i = 0; i < argc; i++)
+ ss_put_share(argv[i]);
+ }
+ break;
+ case aSSGenShare:
+ if (argc != 1)
+ wrong_args("--ss-gen-share KeyID");
+ ss_gen_share(*argv, remusr, opt.outfile);
+ break;
+ case aSSClear:
+ if (argc != 1)
+ wrong_args("--ss-clear KeyID");
+ ss_clear(*argv);
+ break;
case aListPackets:
opt.list_packets=2;
Index: g10/Makefile.am
===================================================================
--- g10/Makefile.am (revision 4803)
+++ g10/Makefile.am (working copy)
@@ -64,6 +64,7 @@ common_source = \
parse-packet.c \
cpr.c \
plaintext.c \
+ share.c \
sig-check.c \
keylist.c \
pkglue.c pkglue.h
@@ -100,7 +101,9 @@ gpg2_SOURCES = gpg.c \
photoid.c photoid.h \
call-agent.c call-agent.h \
card-util.c \
- exec.c exec.h
+ exec.c exec.h \
+ secret-sharing.c secret-sharing.h \
+ call-ssd.c call-ssd.h
gpgv2_SOURCES = gpgv.c \
$(common_source) \
Index: common/openpgpdefs.h
===================================================================
--- common/openpgpdefs.h (revision 4803)
+++ common/openpgpdefs.h (working copy)
@@ -43,6 +43,7 @@ typedef enum
PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */
PKT_MDC = 19, /* Manipulation detection code packet. */
PKT_COMMENT = 61, /* new comment packet (GnuPG specific). */
+ PKT_GPG_SHARE = 62, /* internal packet for transfering a share */
PKT_GPG_CONTROL = 63 /* internal control packet (GnuPG specific). */
}
pkttype_t;
Index: am/cmacros.am
===================================================================
--- am/cmacros.am (revision 4803)
+++ am/cmacros.am (working copy)
@@ -41,6 +41,9 @@ endif
if GNUPG_SCDAEMON_PGM
AM_CPPFLAGS += -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
endif
+if GNUPG_SSD_PGM
+AM_CPPFLAGS += -DGNUPG_DEFAULT_SSD="\"@GNUPG_SSD_PGM@\""
+endif
if GNUPG_DIRMNGR_PGM
AM_CPPFLAGS += -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
endif
Index: Makefile.am
===================================================================
--- Makefile.am (revision 4803)
+++ Makefile.am (working copy)
@@ -54,6 +54,11 @@ scd = scd
else
scd =
endif
+if BUILD_SSD
+ssd = ssd
+else
+ssd =
+endif
if BUILD_TOOLS
tools = tools
else
@@ -72,7 +77,7 @@ tests = tests
endif
SUBDIRS = m4 gl include jnlib common ${kbx} \
- ${gpg} ${keyserver} ${sm} ${agent} ${scd} ${tools} po ${doc} ${tests}
+ ${gpg} ${keyserver} ${sm} ${agent} ${scd} ${ssd} ${tools} po ${doc} ${tests}
dist_doc_DATA = README
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
URL: </pipermail/attachments/20080731/4a373a9c/attachment-0001.pgp>
More information about the Gnupg-devel
mailing list