[PATCH] Distant signatures
Petr Baudis
pasky at pasky.ji.cz
Wed Jul 3 06:33:02 CEST 2002
Hello,
this patch implements "distant signatures". That is, hash of some file can be
saved in raw form first, and signed later. Note that only detached signatures
are supported, and they can only be loaded in raw binary form now (altough
armored save is supported).
This is very useful in ie. this scenario: on server A, you have huuuge file
and on server B, you have your private key and you would like to sign file from
server A with it, but you don't want to download the file from server A to
server B, and you don't trust server A enough to transfer your private key
there. Normally, you're out of luck, but with this patch, you can create
message digest (hash) of the huge file on server A (this needs no key or so),
which is about 90 bytes usually, transfer it to server B and there you encrypt
it with your key - the resulting signature is valid signature for huge file on
server A.
To generate the binary dump of message digest, run gpg --export-md hugefile.
Then transfer hugefile.sig wherever you want, and run gpg --import-md [-a]
hugefile.sig. Now, in hugefile.sig.{asc,sig} there's valid signature for
hugefile. Hopefully.
Note that by now, the message digest will be always SHA1, thus it will make
sense only when your key on server B will be a DSA one!
The patch is also on the web at http://pasky.ji.cz/~pasky/dev/gnupg/.
The patch is kinda hackish and dirty; mainly due to my lack of time, I just
wanted to get this work, possibly relatively cleanly, but still there may be
endianity issues, problems with exotic keys etc. Basically, this patch is
intended mainly as a draft, How Things Could Be Done, possibly inspiration for
someone with spare time and better gnupg internals knowledge. I think such
feature should certainly be implemented, though, as it can be very useful
frequently, i.e. for package maintainers who don't want to transfer tarballs
here and there over their poor home connection, but also don't want to hand out
their sign keys.
Kind regards,
--
Petr "Pasky" Baudis
* ELinks maintainer * IPv6 guy (XS26 co-coordinator)
* IRCnet operator * FreeCiv AI occassional hacker
.
"Capitalism is the extraordinary belief that the nastiest of men,
for the nastiest of reasons, will somehow work for the benefit of
us all." -- John Maynard Keynes
.
Public PGP key && geekcode && homepage: http://pasky.ji.cz/~pasky/
-------------- next part --------------
This patch implements "distant signatures". That is, hash of some file can be
saved in raw form first, and signed later. Note that only detached signatures
are supported, and they can only be loaded in raw binary form now (altough
armored save is supported).
This is very useful in ie. this scenario: on server A, you have huuuge file and
on server B, you have your private key and you would like to sign file from
server A with it, but you don't want to download the file from server A to
server B, and you don't trust server A enough to transfer your private key
there. Normally, you're out of luck, but with this patch, you can create
message digest (hash) of the huge file on server A (this needs no key or so),
which is about 90 bytes usually, transfer it to server B and there you encrypt
it with your key - the resulting signature is valid signature for huge file on
server A.
To generate the binary dump of message digest, run gpg --export-md hugefile.
Then transfer hugefile.sig wherever you want, and run gpg --import-md [-a]
hugefile.sig. Now, in hugefile.sig.{asc,sig} there's valid signature for
hugefile. Hopefully.
Note that by now, the message digest will be always SHA1, thus it will make
sense only when your key on server B will be a DSA one!
Good luck!
Petr Baudis
<pasky at ji.cz>
http://pasky.ji.cz/~pasky/dev/gnupg/
diff -ru gnupg-1.0.6/cipher/dynload.c gnupg-1.0.6+distantsignatures/cipher/dynload.c
--- gnupg-1.0.6/cipher/dynload.c Sun Apr 29 15:45:42 2001
+++ gnupg-1.0.6+distantsignatures/cipher/dynload.c Wed Jul 3 04:03:11 2002
@@ -400,6 +400,8 @@
const char *(**r_get_info)( int, size_t*,byte**, int*, int*,
void (**)(void*),
void (**)(void*,byte*,size_t),
+ byte *(**)(void*,size_t*),
+ void (**)(void*,byte*,size_t),
void (**)(void*),byte *(**)(void*)) )
{
EXTLIST r;
diff -ru gnupg-1.0.6/cipher/dynload.h gnupg-1.0.6+distantsignatures/cipher/dynload.h
--- gnupg-1.0.6/cipher/dynload.h Sun Apr 29 15:46:13 2001
+++ gnupg-1.0.6+distantsignatures/cipher/dynload.h Wed Jul 3 04:02:39 2002
@@ -31,6 +31,8 @@
int *algo,
const char *(**r_get_info)( int, size_t*,byte**, int*, int*,
void (**)(void*),
+ void (**)(void*,byte*,size_t),
+ byte *(**)(void*,size_t*),
void (**)(void*,byte*,size_t),
void (**)(void*),byte *(**)(void*)) );
diff -ru gnupg-1.0.6/cipher/md.c gnupg-1.0.6+distantsignatures/cipher/md.c
--- gnupg-1.0.6/cipher/md.c Sun Apr 29 15:48:16 2001
+++ gnupg-1.0.6+distantsignatures/cipher/md.c Wed Jul 3 04:29:06 2002
@@ -43,6 +43,8 @@
int asnlen;
int mdlen;
void (*init)( void *c );
+ void (*import)( void *c, byte *buf, size_t len);
+ byte *(*export)( void *c, size_t *len);
void (*write)( void *c, byte *buf, size_t nbytes );
void (*final)( void *c );
byte *(*read)( void *c );
@@ -58,6 +60,8 @@
const char *(*get_info)( int, size_t*,byte**, int*, int*,
void (**)(void*),
void (**)(void*,byte*,size_t),
+ byte *(**)(void*,size_t*),
+ void (**)(void*,byte*,size_t),
void (**)(void*),byte *(**)(void*)) )
{
struct md_digest_list_s *r;
@@ -66,7 +70,8 @@
r->algo = algo,
r->name = (*get_info)( algo, &r->contextsize,
&r->asnoid, &r->asnlen, &r->mdlen,
- &r->init, &r->write, &r->final, &r->read );
+ &r->init, &r->import, &r->export,
+ &r->write, &r->final, &r->read );
if( !r->name ) {
m_free(r);
r = NULL;
@@ -94,6 +99,8 @@
const char *(*get_info)( int, size_t*,byte**, int*, int*,
void (**)(void*),
void (**)(void*,byte*,size_t),
+ byte *(**)(void*,size_t*),
+ void (**)(void*,byte*,size_t),
void (**)(void*),byte *(**)(void*));
if( !initialized ) {
@@ -279,6 +286,54 @@
if( a->debug )
md_start_debug( b, "unknown" );
return b;
+}
+
+
+void
+md_import( MD_HANDLE a, int algo, byte *buf, size_t len )
+{
+ struct md_digest_list_s *r;
+
+ if( !algo ) { /* return the first algorithm */
+ if( (r=a->list) ) {
+ if( r->next )
+ log_debug("more than algorithm in md_import(0)\n");
+ (*r->import)( &r->context.c, buf, len );
+ return;
+ }
+ BUG();
+ }
+ else {
+ for(r=a->list; r; r = r->next )
+ if( r->algo == algo ) {
+ (*r->import)( &r->context.c, buf, len );
+ return;
+ }
+ }
+ BUG();
+ return;
+}
+
+byte *
+md_export( MD_HANDLE a, int algo, size_t *len )
+{
+ struct md_digest_list_s *r;
+
+ if( !algo ) { /* return the first algorithm */
+ if( (r=a->list) ) {
+ if( r->next )
+ log_debug("more than algorithm in md_import(0)\n");
+ return (*r->export)( &r->context.c, len );
+ }
+ }
+ else {
+ for(r=a->list; r; r = r->next )
+ if( r->algo == algo ) {
+ return (*r->export)( &r->context.c, len );
+ }
+ }
+ BUG();
+ return NULL;
}
diff -ru gnupg-1.0.6/cipher/md5.c gnupg-1.0.6+distantsignatures/cipher/md5.c
--- gnupg-1.0.6/cipher/md5.c Sat Apr 28 20:52:39 2001
+++ gnupg-1.0.6+distantsignatures/cipher/md5.c Wed Jul 3 04:13:28 2002
@@ -44,8 +44,8 @@
typedef struct {
u32 A,B,C,D; /* chaining variables */
u32 nblocks;
+ u32 count;
byte buf[64];
- int count;
} MD5_CONTEXT;
@@ -61,6 +61,25 @@
ctx->count = 0;
}
+void
+md5_import( MD5_CONTEXT *hd, byte *buf, size_t len )
+{
+ if (len < 64 + 24) return;
+ memcpy(hd->buf, buf, 64);
+ memcpy(hd, buf + 64, 24);
+}
+
+byte *
+md5_export( MD5_CONTEXT *hd, size_t *len )
+{
+ byte *buf = m_alloc(64 + 24);
+
+ if (!buf) return NULL;
+ memcpy(buf, hd->buf, 64);
+ memcpy(buf + 64, hd, 24);
+ *len = 64 + 24;
+ return buf;
+}
@@ -344,6 +363,8 @@
md5_get_info( int algo, size_t *contextsize,
byte **r_asnoid, int *r_asnlen, int *r_mdlen,
void (**r_init)( void *c ),
+ void (**r_import)( void *c, byte *buf, size_t len ),
+ byte *(**r_export)( void *c, size_t *len ),
void (**r_write)( void *c, byte *buf, size_t nbytes ),
void (**r_final)( void *c ),
byte *(**r_read)( void *c )
@@ -361,6 +382,8 @@
*r_asnlen = DIM(asn);
*r_mdlen = 16;
*(void (**)(MD5_CONTEXT *))r_init = md5_init;
+ *(void (**)(MD5_CONTEXT *, byte*, size_t))r_import= md5_import;
+ *(byte *(**)(MD5_CONTEXT *, size_t*))r_export = md5_export;
*(void (**)(MD5_CONTEXT *, byte*, size_t))r_write = md5_write;
*(void (**)(MD5_CONTEXT *))r_final = md5_final;
*(byte *(**)(MD5_CONTEXT *))r_read = md5_read;
--- gnupg-1.0.6/cipher/rmd.h Sat Apr 28 18:31:26 2001
+++ gnupg-1.0.6+distantsignatures/cipher/rmd.h Wed Jul 3 03:02:41 2002
@@ -25,8 +25,8 @@
typedef struct {
u32 h0,h1,h2,h3,h4;
u32 nblocks;
+ u32 count;
byte buf[64];
- int count;
} RMD160_CONTEXT;
void rmd160_init( RMD160_CONTEXT *hd );
diff -ru gnupg-1.0.6/cipher/rmd160.c gnupg-1.0.6+distantsignatures/cipher/rmd160.c
--- gnupg-1.0.6/cipher/rmd160.c Sat Apr 28 20:52:40 2001
+++ gnupg-1.0.6+distantsignatures/cipher/rmd160.c Wed Jul 3 04:11:07 2002
@@ -166,6 +166,26 @@
hd->count = 0;
}
+void
+rmd160_import( RMD160_CONTEXT *hd, byte *buf, size_t len )
+{
+ if (len < 64 + 28) return;
+ memcpy(hd->buf, buf, 64);
+ memcpy(hd, buf + 64, 28);
+}
+
+byte *
+rmd160_export( RMD160_CONTEXT *hd, size_t *len )
+{
+ byte *buf = m_alloc(64 + 28);
+
+ if (!buf) return NULL;
+ memcpy(buf, hd->buf, 64);
+ memcpy(buf + 64, hd, 28);
+ *len = 64 + 28;
+ return buf;
+}
+
/****************
@@ -562,6 +582,8 @@
rmd160_get_info( int algo, size_t *contextsize,
byte **r_asnoid, int *r_asnlen, int *r_mdlen,
void (**r_init)( void *c ),
+ void (**r_import)( void *c, byte *buf, size_t len ),
+ byte *(**r_export)( void *c, size_t *len ),
void (**r_write)( void *c, byte *buf, size_t nbytes ),
void (**r_final)( void *c ),
byte *(**r_read)( void *c )
@@ -579,6 +601,8 @@
*r_asnlen = DIM(asn);
*r_mdlen = 20;
*(void (**)(RMD160_CONTEXT *))r_init = rmd160_init;
+ *(void (**)(RMD160_CONTEXT *, byte*, size_t))r_import= rmd160_import;
+ *(byte *(**)(RMD160_CONTEXT *, size_t*))r_export = rmd160_export;
*(void (**)(RMD160_CONTEXT *, byte*, size_t))r_write = rmd160_write;
*(void (**)(RMD160_CONTEXT *))r_final = rmd160_final;
*(byte *(**)(RMD160_CONTEXT *))r_read = rmd160_read;
diff -ru gnupg-1.0.6/cipher/sha1.c gnupg-1.0.6+distantsignatures/cipher/sha1.c
--- gnupg-1.0.6/cipher/sha1.c Sat Apr 28 20:52:40 2001
+++ gnupg-1.0.6+distantsignatures/cipher/sha1.c Wed Jul 3 04:09:55 2002
@@ -45,8 +45,8 @@
typedef struct {
u32 h0,h1,h2,h3,h4;
u32 nblocks;
+ u32 count;
byte buf[64];
- int count;
} SHA1_CONTEXT;
static void
@@ -73,6 +73,26 @@
hd->count = 0;
}
+void
+sha1_import( SHA1_CONTEXT *hd, byte *buf, size_t len )
+{
+ if (len < 64 + 28) return;
+ memcpy(hd->buf, buf, 64);
+ memcpy(hd, buf + 64, 28);
+}
+
+byte *
+sha1_export( SHA1_CONTEXT *hd, size_t *len )
+{
+ byte *buf = m_alloc(64 + 28);
+
+ if (!buf) return NULL;
+ memcpy(buf, hd->buf, 64);
+ memcpy(buf + 64, hd, 28);
+ *len = 64 + 28;
+ return buf;
+}
+
/****************
* Transform the message X which consists of 16 32-bit-words
@@ -335,6 +355,8 @@
sha1_get_info( int algo, size_t *contextsize,
byte **r_asnoid, int *r_asnlen, int *r_mdlen,
void (**r_init)( void *c ),
+ void (**r_import)( void *c, byte *buf, size_t len ),
+ byte *(**r_export)( void *c, size_t *len ),
void (**r_write)( void *c, byte *buf, size_t nbytes ),
void (**r_final)( void *c ),
byte *(**r_read)( void *c )
@@ -351,6 +373,8 @@
*r_asnlen = DIM(asn);
*r_mdlen = 20;
*(void (**)(SHA1_CONTEXT *))r_init = sha1_init;
+ *(void (**)(SHA1_CONTEXT *, byte*, size_t))r_import= sha1_import;
+ *(byte *(**)(SHA1_CONTEXT *, size_t*))r_export = sha1_export;
*(void (**)(SHA1_CONTEXT *, byte*, size_t))r_write = sha1_write;
*(void (**)(SHA1_CONTEXT *))r_final = sha1_final;
*(byte *(**)(SHA1_CONTEXT *))r_read = sha1_read;
diff -ru gnupg-1.0.6/g10/g10.c gnupg-1.0.6+distantsignatures/g10/g10.c
--- gnupg-1.0.6/g10/g10.c Mon May 28 09:02:14 2001
+++ gnupg-1.0.6+distantsignatures/g10/g10.c Wed Jul 3 04:26:13 2002
@@ -109,6 +109,8 @@
aGenRandom,
aPipeMode,
aRefreshCaches,
+ aImportMD,
+ aExportMD,
oTextmode,
oFingerprint,
@@ -218,6 +220,8 @@
{ aSign, "sign", 256, N_("|[file]|make a signature")},
{ aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature") },
{ aDetachedSign, "detach-sign", 256, N_("make a detached signature")},
+ { aExportMD, "export-md", 256, N_("make a detached signature with saved raw message digest data")},
+ { aImportMD, "import-md", 256, N_("make a detached signature from saved raw message digest data")},
{ aEncr, "encrypt", 256, N_("encrypt data")},
{ aSym, "symmetric", 256, N_("encryption only with symmetric cipher")},
{ aStore, "store", 256, N_("store only")},
@@ -605,6 +609,7 @@
STRLIST nrings=NULL, sec_nrings=NULL;
armor_filter_context_t afx;
int detached_sig = 0;
+ int md_import = 0, md_export = 0;
FILE *configfp = NULL;
char *configname = NULL;
unsigned configlineno;
@@ -770,6 +775,8 @@
case aDeleteKey: set_cmd( &cmd, aDeleteKey); greeting=1; break;
case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break;
+ case aImportMD: detached_sig = md_import = 1; set_cmd( &cmd, aSign ); break;
+ case aExportMD: detached_sig = md_export = 1; set_cmd( &cmd, aSign ); break;
case aSym: set_cmd( &cmd, aSym); break;
case aDecrypt: set_cmd( &cmd, aDecrypt); break;
@@ -1213,7 +1220,7 @@
strcpy(sl->d, fname);
}
}
- if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) )
+ if( (rc = sign_file( sl, detached_sig, locusr, (md_import << 1) + md_export, 0, NULL, NULL)) )
log_error("signing failed: %s\n", g10_errstr(rc) );
free_strlist(sl);
break;
@@ -1227,7 +1234,7 @@
}
else
sl = NULL;
- if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) )
+ if( (rc = sign_file(sl, detached_sig, locusr, 0, 1, remusr, NULL)) )
log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), g10_errstr(rc) );
free_strlist(sl);
break;
diff -ru gnupg-1.0.6/g10/main.h gnupg-1.0.6+distantsignatures/g10/main.h
--- gnupg-1.0.6/g10/main.h Thu Mar 8 15:06:21 2001
+++ gnupg-1.0.6+distantsignatures/g10/main.h Wed Jul 3 04:17:08 2002
@@ -79,7 +79,7 @@
/*-- sign.c --*/
int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md );
-int sign_file( STRLIST filenames, int detached, STRLIST locusr,
+int sign_file( STRLIST filenames, int detached, STRLIST locusr, int md_ie,
int do_encrypt, STRLIST remusr, const char *outfile );
int clearsign_file( const char *fname, STRLIST locusr, const char *outfile );
diff -ru gnupg-1.0.6/g10/sign.c gnupg-1.0.6+distantsignatures/g10/sign.c
--- gnupg-1.0.6/g10/sign.c Tue Mar 27 16:23:03 2001
+++ gnupg-1.0.6+distantsignatures/g10/sign.c Wed Jul 3 05:02:03 2002
@@ -241,7 +241,7 @@
* uncompressed, non-armored and in binary mode.
*/
int
-sign_file( STRLIST filenames, int detached, STRLIST locusr,
+sign_file( STRLIST filenames, int detached, STRLIST locusr, int md_ie,
int encrypt, STRLIST remusr, const char *outfile )
{
const char *fname;
@@ -272,7 +272,7 @@
if( filenames ) {
fname = filenames->d;
- multifile = !!filenames->next;
+ multifile = filenames->next && md_ie != 2;
}
else
fname = NULL;
@@ -280,10 +280,13 @@
if( fname && filenames->next && (!detached || encrypt) )
log_bug("multiple files can only be detached signed");
- if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
- goto leave;
- if( !old_style )
- old_style = only_old_style( sk_list );
+ if( md_ie != 1 ) {
+ /* We'd still need secret key, that is bad. */
+ if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
+ goto leave;
+ if( !old_style )
+ old_style = only_old_style( sk_list );
+ }
if( encrypt ) {
if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC )) )
@@ -314,26 +317,14 @@
else if( (rc = open_outfile( fname, opt.armor? 1: detached? 2:0, &out )))
goto leave;
- /* prepare to calculate the MD over the input */
- if( opt.textmode && !outfile )
- iobuf_push_filter( inp, text_filter, &tfx );
- mfx.md = md_open(0, 0);
-
- for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
- PKT_secret_key *sk = sk_rover->sk;
- md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version ));
- }
-
- if( !multifile )
- iobuf_push_filter( inp, md_filter, &mfx );
-
+ /* set up output */
if( detached && !encrypt && !opt.rfc1991 )
afx.what = 2;
if( opt.armor && !outfile )
iobuf_push_filter( out, armor_filter, &afx );
#ifdef ENABLE_COMMENT_PACKETS
- else {
+ else if ( md_ie != 1 ) {
write_comment( out, "#created by GNUPG v" VERSION " ("
PRINTABLE_OS_NAME ")");
if( opt.comment_string )
@@ -358,6 +349,41 @@
}
}
+ /* open md handle, it belongs to preparing for calculation, but we need it
+ * now already */
+ mfx.md = md_open(0, 0);
+
+ if( md_ie != 1 ) {
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ PKT_secret_key *sk = sk_rover->sk;
+ md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version ));
+ }
+ } else {
+ md_enable(mfx.md, DIGEST_ALGO_SHA1); /* XXX! */
+ }
+
+ /* if importing, do it now and jump away */
+ if( md_ie == 2 ) {
+ byte copy_buffer[4096];
+ int bytes_copied;
+
+ if ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) == -1) {
+ rc = G10ERR_READ_FILE;
+ log_error("reading input failed: %s\n", g10_errstr(rc));
+ goto leave;
+ }
+ md_import( mfx.md, copy_buffer[0], copy_buffer + 1, bytes_copied - 1 );
+
+ goto write_sk;
+ }
+
+ /* prepare to calculate the MD over the input */
+ if( opt.textmode && !outfile )
+ iobuf_push_filter( inp, text_filter, &tfx );
+
+ if( !multifile )
+ iobuf_push_filter( inp, md_filter, &mfx );
+
if( !detached && !old_style ) {
int skcount=0;
/* loop over the secret certificates and build headers
@@ -491,6 +517,22 @@
/* catch errors from above blocks */
if (rc)
goto leave;
+
+ if (md_ie == 1) {
+ byte *buf;
+ size_t len;
+ byte algo = DIGEST_ALGO_SHA1; /* XXX! */
+
+ buf = md_export( mfx.md, 0, &len );
+ if (!buf) goto leave;
+ if (iobuf_write(out, &algo, 1) == -1 || iobuf_write(out, buf, len) == -1) {
+ rc = G10ERR_WRITE_FILE;
+ log_error("writing output failed: %s\n", g10_errstr(rc));
+ }
+ goto leave;
+ }
+
+write_sk:
/* loop over the secret certificates */
for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
diff -ru gnupg-1.0.6/include/cipher.h gnupg-1.0.6+distantsignatures/include/cipher.h
--- gnupg-1.0.6/include/cipher.h Sat Apr 28 19:50:00 2001
+++ gnupg-1.0.6+distantsignatures/include/cipher.h Wed Jul 3 03:39:10 2002
@@ -106,6 +106,8 @@
MD_HANDLE md_open( int algo, int secure );
void md_enable( MD_HANDLE hd, int algo );
MD_HANDLE md_copy( MD_HANDLE a );
+void md_import( MD_HANDLE a, int algo, byte *buf, size_t len );
+byte *md_export( MD_HANDLE a, int algo, size_t *len );
void md_reset( MD_HANDLE a );
void md_close(MD_HANDLE a);
void md_write( MD_HANDLE a, byte *inbuf, size_t inlen);
More information about the Gnupg-devel
mailing list