Signing as one member of a set of keys
This program can be used by anonymous contributors to release partial information about their identity - they can show that they are someone from a list of PGP key holders, without revealing which member of the list they are. Maybe it can help in the recent controvery over the identity of anonymous posters. It's a fairly low-level program that should be wrapped in a nicer UI. I'll send a couple of perl scripts later that make it easier to use. === /* Implementation of ring signatures from * http://theory.lcs.mit.edu/~rivest/RivestShamirTauman-HowToLeakASecret.pdf * by Rivest, Shamir and Tauman * * This creates and verifies a signature such that it was produced from * one of a fixed set of RSA keys. * * It requires the openssl library to build, which is available from * www.openssl.org. * * This program takes a PGP public key ring file which holds a set of * old-style RSA public keys. It creates and verifies signatures which * are such that they were issued by one of the keys in that file, but * there is no way to tell which one did it. In this way the signer can * leak partial information about his identity - that he is one member * of a selected set of signers. * * To sign, the signer must also give a PGP secret key file which holds * one key (actually the program ignores any keys past the first). * That key should be the secret part of one of the keys in the public * key file. Also, it should be set to have no passphrase - it is too * complicated for a simple program like this to try to untangle PGP * passphrases. So set your key to have no passphrase, then run this * program, then set it back. * * The program outputs the signature in the form of a list of big numbers, * base64 encoded. There will be as many numbers as there were keys in * the public key file. So signatures are quite large in this scheme, * proportional to the number of keys in the group that the signature * comes from. They are also proportional to the largest key in the * group, so all else being equal try not to include really big keys if * you care about size. * * The signature is not appended to the text being signed, it is just * output separately. The signer can combine them manually with some kind * of cut marks so that the recipient can separate out the signature from * the file being signed. Some perl scripts that do this are supposed * to be distributed with the program. (That is what is used to verify * the signature in this file itself.) * * The recipient must use the same PGP public key file that the signer * used. So that may have to be sent along as well. He runs the program * with the PGP file and the file to be verified, and sends the signature * data into stdin (using the "<" character). The program will print * whether the signature is good or not. * * This program was written in just a couple of evenings so it is * a little rough. This is version 0.9 or so - at least it works. * It has only been tested on my Linux system. * * The program is released into the public domain. See the end for * authorship information. */ #include <stdio.h> #include <stdlib.h> #include "openssl/bn.h" #include "openssl/rsa.h" #include "openssl/sha.h" #include "openssl/evp.h" /* Cipher block size; we use Blowfish */ #define CIPHERBLOCK 8 typedef unsigned char uchar; enum { ERR_OK = 0, ERR_BADPKT=-100, ERR_EOF, ERR_SECNOTFOUND, ERR_BADSIG, }; /************************** PGP FILE PARSING ***************************/ /* Read the N and E values from a PGP public key packet */ int rdpgppub( BIGNUM *n, BIGNUM *e, unsigned *bytesused, uchar *buf, unsigned len ) { int nbits, nlen, ebits, elen; unsigned o=2; if (len < 10) return ERR_BADPKT; if (buf[0] == 4) /* Check version 4, 3, or 2 */ o = 0; else if (buf[0] != 2 && buf[0] != 3) /* V2&3 have 2 extra bytes */ return ERR_BADPKT; if (buf[5+o] != 1) /* Check alg - 1 is RSA */ return ERR_BADPKT; nbits = (buf[6+o] << 8) | buf[7+o]; /* Read modulus */ nlen = (nbits + 7)/8; if (len < 10+o+nlen) return ERR_BADPKT; BN_bin2bn(buf+o+8, nlen, n); ebits = (buf[8+o+nlen] << 8) | buf[9+o+nlen]; /* Read exponent */ elen = (ebits + 7)/8; if (len < 10+o+nlen+elen) return ERR_BADPKT; BN_bin2bn(buf+10+o+nlen, elen, e); if (bytesused) *bytesused = 10+o+nlen+elen; return ERR_OK; } /* Read the N, E, D values from a PGP secret key packet with no passphrase */ int rdpgpsec( BIGNUM *n, BIGNUM *e, BIGNUM *d, uchar *buf, unsigned len ) { int err; int nbits, nlen, ebits, elen, dbits, dlen; unsigned o; if ((err = rdpgppub(n, e, &o, buf, len)) < 0) return err; if (len < o+3) return ERR_BADPKT; if (buf[o] != 0) /* Check that packet is unencrypted */ return ERR_BADPKT; dbits = (buf[o+1] << 8) | buf[o+2]; /* Read private exponent */ dlen = (dbits + 7)/8; if (len < o+3+dlen) return ERR_BADPKT; BN_bin2bn(buf+o+3, dlen, d); return ERR_OK; } /* Read the next PGP packet into malloc'd memory */ int getpgppkt( uchar **pbuf, unsigned *plen, int *type, FILE *f ) { int c, c1; uchar *buf; unsigned len; int llen; uchar lbuf[4]; c = fgetc(f); if (c == EOF) return ERR_EOF; if ((c & 0xc0) == 0x80) { /* Old PGP packet */ *type = (c >> 2) & 0xf; llen = c & 0x3; if (llen++==3) return ERR_BADPKT; rdllen: if (llen==3) llen=4; memset (lbuf, 0, 4); if (fread(lbuf+4-llen, 1, llen, f) != llen) return ERR_BADPKT; len = (lbuf[0]<<24) | (lbuf[1]<<16) | (lbuf[2]<<8) | lbuf[3]; } else if ((c & 0xc0) == 0xc0) { /* New PGP packet */ *type = c & 0x3f; c = fgetc(f); if (c == EOF) return ERR_BADPKT; if (c == 0xff) { llen = 4; goto rdllen; } if (c >= 0xe0) return ERR_BADPKT; if (c >= 0xc0) { /* Two byte length */ c1 = fgetc(f); if (c1 == EOF) return ERR_BADPKT; len = (c<<8) + c1 - 0xc000 + 0xc0; } else { /* One byte length */ len = c; } } else { /* Non PGP packet */ return ERR_BADPKT; } buf = malloc(len); if (buf==NULL) return ERR_BADPKT; if (fread(buf, 1, len, f) != len) return ERR_BADPKT; *pbuf = buf; *plen = len; return ERR_OK; } /* Read a PGP key ring and create arrays of all the n, e values */ int rdpgppubring(BIGNUM ***n_arr_ptr, BIGNUM ***e_arr_ptr, int *nkeys_ptr, FILE *f) { int err = ERR_OK; uchar *buf; unsigned len; int type; int nkeys = 0; BIGNUM **n_arr = NULL; BIGNUM **e_arr = NULL; BIGNUM *n, *e; n_arr = malloc(sizeof(BIGNUM *)); e_arr = malloc(sizeof(BIGNUM *)); while (err == ERR_OK) { err = getpgppkt (&buf, &len, &type, f); if (err != ERR_OK) break; if (type == 6) /* public key packet */ { n = BN_new(); e = BN_new(); err = rdpgppub(n, e, NULL, buf, len); if (err != ERR_OK) break; ++nkeys; n_arr = realloc(n_arr, sizeof(BIGNUM *) * nkeys); e_arr = realloc(e_arr, sizeof(BIGNUM *) * nkeys); n_arr[nkeys-1] = n; e_arr[nkeys-1] = e; } free (buf); } if (err != ERR_EOF) return err; err = ERR_OK; *n_arr_ptr = n_arr; *e_arr_ptr = e_arr; *nkeys_ptr = nkeys; return err; } /* Read a PGP secret key file and find the corresponding value in the * array of public key values. Return the d value and the index in the * public key array. */ int rdpgpsecring(BIGNUM *d, int *secindex, BIGNUM **n_arr, BIGNUM **e_arr, int nkeys, FILE *f) { int err = ERR_OK; BIGNUM *n, *e; uchar *buf; unsigned len; int i; int type; err = getpgppkt (&buf, &len, &type, f); if (err != ERR_OK) return err; if (type != 5) /* Secret key packet */ return ERR_BADPKT; n = BN_new(); e = BN_new(); err = rdpgpsec(n, e, d, buf, len); if (err != ERR_OK) return err; for (i=0; i<nkeys; i++) /* Find corresponding public key */ { if (BN_cmp (n, n_arr[i]) == 0 && BN_cmp (e, e_arr[i]) == 0) break; } if (i == nkeys) return ERR_SECNOTFOUND; *secindex = i; return ERR_OK; } /************************** UTILITY FUNCTIONS ***************************/ /* Sort the n and e arrays by increasing n */ int sortkeys (BIGNUM **n, BIGNUM **e, int nkeys) { int i, j; BIGNUM *t; /* Do a bubble sort as the number of keys is not usually very large */ for (i=0; i<nkeys-1; i++) { for (j=i+1; j<nkeys; j++) { if (BN_cmp( n[i], n[j] ) > 0) { t = n[i]; n[i] = n[j]; n[j] = t; t = e[i]; e[i] = e[j]; e[j] = t; } } } return ERR_OK; } /* Hash the file. Should have opened it in ASCII mode so that we have * standard Unix line endings (newlines only). */ int hashfile( uchar md[5], FILE *f ) { char buf[1024]; SHA_CTX sha; SHA_Init(&sha); for ( ; ; ) { if (fgets (buf, sizeof(buf), f) == NULL) break; SHA_Update(&sha, buf, strlen(buf)); } SHA_Final (md, &sha); return ERR_OK; } /* Do an RSA enc/dec, where the input/output value may be larger * than n. In fact, val should be much larger than n or this may fail * to keep val within the desired range. * To decrypt pass d in place of e. */ int rsaencdec( BIGNUM *rslt, BIGNUM *val, BIGNUM *n, BIGNUM *e, BN_CTX *ctx ) { BIGNUM *rem = BN_new(); BIGNUM *newrem = BN_new(); BN_div (NULL, rem, val, n, ctx); BN_mod_exp (newrem, rem, e, n, ctx); BN_sub (rslt, val, rem); BN_add (rslt, rslt, newrem); BN_free (rem); BN_free (newrem); } /************************** SIG CREATE/VERIFY ***************************/ /* Verify a signature. sigs holds the per-key signature values, * hashval is the hash of the data which was signed, valbytes is the * length of the values we work with, several bytes longer than the longest * modulus, and n_arr and e_arr are the RSA public key values, of whic * there are nkeys of them. (There are also nkeys of sigs.) */ int checksig( BIGNUM **sigs, uchar *hashval, int hashvalbytes, int valbytes, BIGNUM **n_arr, BIGNUM **e_arr, int nkeys, BN_CTX *ctx ) { BIGNUM *val = BN_new(); uchar ivec[CIPHERBLOCK]; BF_KEY bf; uchar *sigbuf; uchar *xorbuf; int vallen; int i, j; /* Key cipher with the hash value */ BF_set_key (&bf, hashvalbytes, hashval); /* Init xorbuf to 0's */ xorbuf = malloc(valbytes); memset (xorbuf, 0, valbytes); sigbuf = malloc(valbytes); for (i=0; i<nkeys; i++) { rsaencdec (val, sigs[i], n_arr[i], e_arr[i], ctx); vallen = BN_num_bytes(val); if (vallen > valbytes) { fprintf (stderr, "Bad signature created by signer\n"); exit (3); } /* XOR into buffer */ memset (sigbuf, 0, valbytes); BN_bn2bin (val, sigbuf+valbytes-vallen); for (j=0; j<valbytes; j++) xorbuf[j] ^= sigbuf[j]; memset (ivec, 0, sizeof(ivec)); BF_cbc_encrypt (xorbuf, xorbuf, valbytes, &bf, ivec, BF_ENCRYPT); } BN_free (val); /* xorbuf should be all 0's for the sig to verify */ for (j=0; j<valbytes; j++) if (xorbuf[j] != 0) break; free (xorbuf); free (sigbuf); if (j < valbytes) return ERR_BADSIG; return ERR_OK; } /* Create a signature. psigarray is the returned array of bignums, * of which there are nkeys. hashval is the hash of the data being * signed. d is the RSA private key value for the key at position * secindex. valbytes is the size of the values we are working with, * several bytes greater than the length of the longest modulus. * And n_arr and e_arr are the RSA public key values, of which there * are nkeys. */ int createsig( BIGNUM ***psigarray, uchar *hashval, int hashvalbytes, BIGNUM *d, int secindex, int valbytes, BIGNUM **n_arr, BIGNUM **e_arr, int nkeys, BN_CTX *ctx ) { BIGNUM *val = BN_new(); BIGNUM *bigval = BN_new(); BIGNUM **sigs; BF_KEY bf; uchar ivec[CIPHERBLOCK]; uchar *sigbuf; uchar *lxorbuf; uchar *rxorbuf; int vallen; int i, j; /* Key cipher with the hash value */ BF_set_key (&bf, hashvalbytes, hashval); BN_lshift (bigval, BN_value_one(), valbytes*8); /* Init two xorbufs we will make meet in the middle */ rxorbuf = malloc(valbytes); memset (rxorbuf, 0, valbytes); lxorbuf = malloc(valbytes); memset (lxorbuf, 0, valbytes); memset (ivec, 0, sizeof(ivec)); /* Start the MITM process on the left xorbuf */ BF_cbc_encrypt (lxorbuf, lxorbuf, valbytes, &bf, ivec, BF_DECRYPT); sigbuf = malloc(valbytes); sigs = (BIGNUM **)malloc(nkeys * sizeof(BIGNUM *)); for (i=0; i<secindex; i++) { /* For other keys do a fake value */ sigs[i] = BN_new(); BN_rand_range (sigs[i], bigval); rsaencdec (val, sigs[i], n_arr[i], e_arr[i], ctx); /* Infinitisimal chance that vallen > valbytes with random val */ vallen = BN_num_bytes(val); /* XOR into right xor buf and encrypt */ memset (sigbuf, 0, valbytes); BN_bn2bin (val, sigbuf+valbytes-vallen); for (j=0; j<valbytes; j++) rxorbuf[j] ^= sigbuf[j]; memset (ivec, 0, sizeof(ivec)); BF_cbc_encrypt (rxorbuf, rxorbuf, valbytes, &bf, ivec, BF_ENCRYPT); } for (i=nkeys-1; i>secindex; i--) { /* For other keys do a fake value */ sigs[i] = BN_new(); BN_rand_range (sigs[i], bigval); rsaencdec (val, sigs[i], n_arr[i], e_arr[i], ctx); /* Infinitisimal chance that vallen > valbytes with random val */ vallen = BN_num_bytes(val); /* XOR into left xor buf and decrypt */ memset (sigbuf, 0, valbytes); BN_bn2bin (val, sigbuf+valbytes-vallen); for (j=0; j<valbytes; j++) lxorbuf[j] ^= sigbuf[j]; memset (ivec, 0, sizeof(ivec)); BF_cbc_encrypt (lxorbuf, lxorbuf, valbytes, &bf, ivec, BF_DECRYPT); } /* XOR the two buffers to get the value we must RSA sign */ for (j=0; j<valbytes; j++) lxorbuf[j] ^= rxorbuf[j]; /* Get val as the value we need to do the RSA signature to */ sigs[secindex] = BN_new(); BN_bin2bn (lxorbuf, valbytes, val); rsaencdec (sigs[secindex], val, n_arr[secindex], d, ctx); BN_free (val); BN_free (bigval); free (sigbuf); free (lxorbuf); free (rxorbuf); *psigarray = sigs; return ERR_OK; } /*************************** USER INTERFACE ****************************/ static char *prog; void userr() { fprintf (stderr, "Usage:\n" "To sign (signature data to stdout):\n" " %s -s textfile pubkeyfile seckeyfile\n" "To verify a signature (signature data from stdin):\n" " %s -v textfile pubkeyfile\n", prog, prog); exit (1); } int main (int ac, char **av) { FILE *fpub, *fsec, *fdata; BN_CTX *ctx; BIGNUM *d; BIGNUM **n_arr; BIGNUM **e_arr; BIGNUM **sigarray; uchar md[SHA_DIGEST_LENGTH]; int secindex; int valbytes; int nkeys; int i; int dosign; int err; ctx = BN_CTX_new(); d = BN_new(); prog = av[0]; if (ac < 2) userr(); if (strcmp( av[1], "-s" ) == 0) dosign = 1; else if (strcmp( av[1], "-v" ) == 0) dosign = 0; else userr(); if (ac < (dosign ? 5 : 4) ) userr(); fdata = fopen (av[2], "r"); /* text mode */ if (fdata==NULL) userr(); fpub = fopen (av[3], "rb"); if (fpub==NULL) userr(); if (dosign) { fsec = fopen (av[4], "rb"); if (fsec==NULL) userr(); } err = rdpgppubring(&n_arr, &e_arr, &nkeys, fpub); if (err != ERR_OK) goto error; fclose (fpub); sortkeys (n_arr, e_arr, nkeys); if (dosign) { err = rdpgpsecring(d, &secindex, n_arr, e_arr, nkeys, fsec); if (err != ERR_OK) goto error; fclose (fsec); } /* For our values we use 2^128 times the largest n; mult of CIPHERBLOCK */ valbytes = BN_num_bytes (n_arr[nkeys-1]) + 16; valbytes = ((valbytes+CIPHERBLOCK-1)/CIPHERBLOCK)*CIPHERBLOCK; /* Hash the file to sign/verify */ err = hashfile (md, fdata); if (dosign) { BIO *bio, *b64; uchar *sigbuf; err = createsig (&sigarray, md, sizeof(md), d, secindex, valbytes, n_arr, e_arr, nkeys, ctx); if (err != ERR_OK) goto error; b64 = BIO_new(BIO_f_base64()); bio = BIO_new_fp(stdout, BIO_NOCLOSE); bio = BIO_push(b64, bio); sigbuf = (uchar *)malloc(valbytes); for (i=0; i<nkeys; i++) { memset (sigbuf, 0, valbytes); BN_bn2bin(sigarray[i], sigbuf+valbytes-BN_num_bytes(sigarray[i])); BIO_write(bio, sigbuf, valbytes); } BIO_flush(bio); BIO_free_all(bio); free (sigbuf); } else { /* Read sig data from stdin */ BIO *bio, *b64; uchar *sigbuf; int inlen; b64 = BIO_new(BIO_f_base64()); bio = BIO_new_fp(stdin, BIO_NOCLOSE); bio = BIO_push(b64, bio); sigarray = (BIGNUM **)malloc(nkeys * sizeof(BIGNUM *)); sigbuf = (uchar *)malloc(valbytes); for (i=0; i<nkeys; i++) { if ((inlen = BIO_read(bio, sigbuf, valbytes)) < valbytes) userr(); sigarray[i] = BN_bin2bn (sigbuf, valbytes, NULL); } free (sigbuf); BIO_free_all(bio); err = checksig (sigarray, md, sizeof(md), valbytes, n_arr, e_arr, nkeys, ctx); if (err == ERR_OK) { printf ("Good signature\n"); } else if (err = ERR_BADSIG) { printf ("ERROR: Bad signature\n"); exit (2); } else { goto error; } } error: if (err != ERR_OK) { fprintf (stderr, "Error %d\n", err); exit (1); } exit (0); } /* Signature block! Who wrote this program? Someone from the keys below. -----BEGIN PGP PUBLIC KEY BLOCK----- Version: 2.6.2 mQCNAisbwocAAAED/jitkUnDwPdZk1Rdr0YNT7h7OHd9zbJbTZEQgdEs5LxQ1h4t S+fCbgM6zxfMvCWqWPendS3ikEStq1PMGQKtt0b0/4BafB6dO1ljSBp+UWkw86FL BmCcQkED0CmQOFAEy2p2SURvdO4tExerfUgf+oYQg99v03mvYlVkk3dax7hlAAUR tBBsb2tpQG9ic2N1cmEuY29ttChMYW5jZSBNLiBDb3R0cmVsbCA8bG9raUBuYXRl bHkudWNzZC5lZHU+tDhMYW5jZSBNLiBDb3R0cmVsbCAoYXQgaG9tZSkgPGxjb3R0 cmVsbEBwb3BtYWlsLnVjc2QuZWR1PpkAjQMxav4LAAABBADJkZ8ZP83waDvuf/Lx 4UB7KFSkm9IKKht2FpW5ztHG22EmCuarCYhSB+R7dzIAN4Wx9UcdS3x724ozuoNm bQboiSsSKO41efGcn5zbzFKlLU+Im5utXJ1QtYlbjx6PjnfZiajVAIpgwiekwP7I PxL6V0i4jYlln3dGUYkxK0j29QAFEbQjSWFuIEdvbGRiZXJnIDxpYW5nQGNzLmJl cmtlbGV5LmVkdT60IUlhbiBHb2xkYmVyZyA8aWFuQGN5cGhlcnB1bmtzLmNhPrQk SWFuIEdvbGRiZXJnIDxpYW5AemVyb2tub3dsZWRnZS5jb20+mQCNAi0xHTIAAAEE AKeIU9S010e1AxYy2R379ptHunqM0kRMgWnOwfCnVets8jThr7B87pzFNVj6kBs8 F9TKQdk62JR5Kiq2rVODFSLmN2JThnhfDu/tAYAz8fJsWkxGn5IhcjxkQpfb2LDs 4EBJgWhI9HxIfCvhSkFdrFe9JBfm0KKB5sGoFIWXVYodAAUTtCFQcjBkdWN0IEN5 cGhlciA8YWx0LnNlY3VyaXR5LnBncD6ZAI0CL7SNAQAAAQQAruhfQV42dxod9rW/ 5XtIxS+ICbyoHyitYbTO+Nrhm1KsdWD88zCmlSHji3kRED2WW1EF0e7fT5hKHxrV 1i3Vc4PzeeSfOP2uG2kUA+rLLCUtWE1Vjc9UvIxtQvDbtoQAZz5K0WqsOhRDT6P1 BT9gf8jJU32sFonKGwgMRScZrzUABRG0KEJlbiBMYXVyaWUgPGJlbkBnb256by5i ZW4uYWxncm91cC5jby51az60HEJlbiBMYXVyaWUgPGJlbkBjcnlwdGl4Lm9yZz60 HkJlbiBMYXVyaWUgPGJlbkBhbGdyb3VwLmNvLnVrPpkAjQIqrDZMAAABBADCljOd puRFotKHCuLtXAtjz8h0+rWr5PTwt0weVwWA3oH93bJXUaZ4yY9a/mjtPBRTvkCx CPcLQar39Tzz+Yi1+7A9riFZSR9eDD7clbY5vWSm/CTLQlu+NOOQYLRwQvckN1e7 1zVeYzOnRNqHJx+ACP6QRaxiyTyoEwOvWCFMNwAFEbQmSGFsIEZpbm5leSA8NzQw NzYuMTA0MUBjb21wdXNlcnZlLmNvbT6ZAI0DMxu7SwAAAQQApJs0Xcl4sIUngK32 4f1sQ9H7GGctVkjxynj5b3or0rhiI2u/OowQCj7VwnCdoM/Itqz6HUiVRO8D+eHz XQRpAXKoeEgxzMN45iNvgneU0/VsN49bCDCM7JPHau2cMm4xWhQUA6s59CxhR+Y2 cMWxLpwcXDJ//+YoNZtIfcgAK9EABRG0HkVyaWMgWW91bmcgPGVheUBjcnlwdHNv ZnQuY29tPpkAjQIty/OuAAABBACkXvI/UFKi+Dw0ErJYkykNMk5HSy/ARQZu3NN0 QvTpeTz/So/0bZ7HcRTE7Vu8JDME6WNYxdtx0fseeC6zqYvIoOhxJol1VVrjQckY 0jIM/I765zDNP7SDgjwIDTMYgDVFqq3rTxFgmRPbC28pZCa6zneeGSslwU8Pw+wC +7uKsQAFEbQhQ29saW4gUGx1bWIgPGNvbGluQG55eC5jcy5kdS5lZHU+ =/CJG -----END PGP PUBLIC KEY BLOCK----- ++multisig v1.0 pEsBwalpBRxWyJR8tkYm6qR27UW9IT6Vg8SlOHIsEkk04RJvoSy0cy4ISFCq6vDX 5ub6c+MYi/UoyR6tI7oqpMu1abcXWm2DkfDiCsD6jQddVkiiYdG7Bih8JWdWmp5l AgzqUoz14671/ezmWSrPNsTNKV96+ZLEanZsqfkpQcnZpLkWVpJzQFe0VgDQ64b2 +e2efrbknLFq0FTdX7Sh3qzAfzNYYgADmeOxDoTm9sb6T0fULf1P7mjiN2LZXuEW m/8QvksaQi9KGa/0xN2m0heNtS1cfsTa+NJz8XYyG/tnMy7+mvI3c3lrnz+6Dpyp pbNwaX+12VcqtfNec9faoq8RJgFxmSO/ZfMOGM8cFBQ75ZOaoBJP5ObHZ/63FFh5 Wh5GzwJjQs0vLwpM3iF6G+IixEqAQYisUdCopP1wXCLgltDM6l7jRlXxNDj0AXQ1 eQJolo32vemcy8Z8GAn5tpQHmJwpdzZpboWRQY53pD4mVnEMN4GBC1mhbbI2z+Oh lPglqmmy3p4D+psNU1rlNv6yH/L0PgcuW7taVpbopjl4HLuJdWcKHJlXish3D/jb eoQ856fYFZ/omGiO9x1D0BsnGFLZVWob4OIZRzO/Pc49VIhFy5NsV2zuozStId89 [...] */
Very nice. Nice plausible set of candidate authors also: pub 1022/5AC7B865 1992/12/01 loki@obscura.com pub 1024/2B48F6F5 1996/04/10 Ian Goldberg <iang@cs.berkeley.edu> pub 1024/97558A1D 1994/01/10 Pr0duct Cypher <alt.security.pgp> pub 1024/2719AF35 1995/05/13 Ben Laurie <ben@gonzo.ben.algroup.co.uk> pub 1024/58214C37 1992/09/08 Hal Finney <74076.1041@compuserve.com> pub 1024/C8002BD1 1997/03/04 Eric Young <eay@cryptsoft.com> pub 1024/FBBB8AB1 1994/05/07 Colin Plumb <colin@nyx.cs.du.edu> Wonder if we can figure out who is most likely author based on coding style from such a small set. It has (8 char) TABs but other wise BSD indentation style (BSD normally 4 spaces). Also someone who likes triply indirected pointers ***blah in there. Has local variables inside even *if code blocks* eg, inside main() (most people avoid that, preferring to declare variables at the top of a function, and historically I think some older gcc / gdb couldn't debug those variables if I recall). Very funky use of goto in getpgppkt, hmmm. Somewhat concise coding and variable names. Off the cuff guess based on coding without looking at samples of code to remind, probably Colin or Ian. Of course (Lance Cottrell/Ian Goldberg/Pr0duct Cypher/Ben Laurie/Hal Finney/Eric Young/Colin Plumb) possibly deviated or mimicked one of their coding styles. Kind of interesting to see a true nym in there also. Also the Cc -- Coderpunks lives? I think the Cc coderpunks might be a clue also, I think some of these people would know it died. I think that points more at Colin. Other potential avenue might be implementation mistake leading to failure of the scheme to robustly make undecidable which of the set is the true author, given alpha code. Adam On Fri, Aug 09, 2002 at 03:52:56AM +0000, Anonymous User wrote:
This program can be used by anonymous contributors to release partial information about their identity - they can show that they are someone from a list of PGP key holders, without revealing which member of the list they are. Maybe it can help in the recent controvery over the identity of anonymous posters. It's a fairly low-level program that should be wrapped in a nicer UI. I'll send a couple of perl scripts later that make it easier to use.
On Fri, 9 Aug 2002, Anonymous User wrote:
This program can be used by anonymous contributors to release partial information about their identity - they can show that they are someone from a list of PGP key holders, without revealing which member of the list they are. Maybe it can help in the recent controvery over the identity of anonymous posters. It's a fairly low-level program that should be wrapped in a nicer UI. I'll send a couple of perl scripts later that make it easier to use.
===
Most delightful. Thank you for reminding us that Cypherpunks do indeed write code. More comments in a bit. [MW SNIP]
++multisig v1.0 pEsBwalpBRxWyJR8tkYm6qR27UW9IT6Vg8SlOHIsEkk04RJvoSy0cy4ISFCq6vDX 5ub6c+MYi/UoyR6tI7oqpMu1abcXWm2DkfDiCsD6jQddVkiiYdG7Bih8JWdWmp5l AgzqUoz14671/ezmWSrPNsTNKV96+ZLEanZsqfkpQcnZpLkWVpJzQFe0VgDQ64b2 +e2efrbknLFq0FTdX7Sh3qzAfzNYYgADmeOxDoTm9sb6T0fULf1P7mjiN2LZXuEW m/8QvksaQi9KGa/0xN2m0heNtS1cfsTa+NJz8XYyG/tnMy7+mvI3c3lrnz+6Dpyp pbNwaX+12VcqtfNec9faoq8RJgFxmSO/ZfMOGM8cFBQ75ZOaoBJP5ObHZ/63FFh5 Wh5GzwJjQs0vLwpM3iF6G+IixEqAQYisUdCopP1wXCLgltDM6l7jRlXxNDj0AXQ1 eQJolo32vemcy8Z8GAn5tpQHmJwpdzZpboWRQY53pD4mVnEMN4GBC1mhbbI2z+Oh lPglqmmy3p4D+psNU1rlNv6yH/L0PgcuW7taVpbopjl4HLuJdWcKHJlXish3D/jb eoQ856fYFZ/omGiO9x1D0BsnGFLZVWob4OIZRzO/Pc49VIhFy5NsV2zuozStId89 [...] */
That [...] you see is an artifact of the anonymous remailer you were using. Mixmaster, I believe, gives the option to truncate messages which appear to include binary encoded data. PGP messages are explicitly allowed to be sent. Immediate problem: we can't verify your signature. Short term solution: find a remailer that allows binary posting. Long term solution: perhaps contact the Mixmaster authors and ask them to explicitly allow "multisig" data? -MW-
Of course, the paranoid amonsgt us now believe that Mr. Back wrote the code, and is engaging in a little misdirection below. "Thanks for making the analysis easy!" ;) On Fri, Aug 09, 2002 at 08:11:15PM +0100, Adam Back wrote: | Very nice. | | Nice plausible set of candidate authors also: | | pub 1022/5AC7B865 1992/12/01 loki@obscura.com | pub 1024/2B48F6F5 1996/04/10 Ian Goldberg <iang@cs.berkeley.edu> | pub 1024/97558A1D 1994/01/10 Pr0duct Cypher <alt.security.pgp> | pub 1024/2719AF35 1995/05/13 Ben Laurie <ben@gonzo.ben.algroup.co.uk> | pub 1024/58214C37 1992/09/08 Hal Finney <74076.1041@compuserve.com> | pub 1024/C8002BD1 1997/03/04 Eric Young <eay@cryptsoft.com> | pub 1024/FBBB8AB1 1994/05/07 Colin Plumb <colin@nyx.cs.du.edu> | | Wonder if we can figure out who is most likely author based on coding | style from such a small set. | | It has (8 char) TABs but other wise BSD indentation style (BSD | normally 4 spaces). Also someone who likes triply indirected pointers | ***blah in there. Has local variables inside even *if code blocks* | eg, inside main() (most people avoid that, preferring to declare | variables at the top of a function, and historically I think some | older gcc / gdb couldn't debug those variables if I recall). Very | funky use of goto in getpgppkt, hmmm. Somewhat concise coding and | variable names. | | Off the cuff guess based on coding without looking at samples of code | to remind, probably Colin or Ian. | | Of course (Lance Cottrell/Ian Goldberg/Pr0duct Cypher/Ben Laurie/Hal | Finney/Eric Young/Colin Plumb) possibly deviated or mimicked one of | their coding styles. Kind of interesting to see a true nym in there | also. | | Also the Cc -- Coderpunks lives? I think the Cc coderpunks might be a | clue also, I think some of these people would know it died. I think | that points more at Colin. | | Other potential avenue might be implementation mistake leading to | failure of the scheme to robustly make undecidable which of the set is | the true author, given alpha code. | | Adam | | On Fri, Aug 09, 2002 at 03:52:56AM +0000, Anonymous User wrote: | > This program can be used by anonymous contributors to release partial | > information about their identity - they can show that they are someone | > from a list of PGP key holders, without revealing which member of the | > list they are. Maybe it can help in the recent controvery over the | > identity of anonymous posters. It's a fairly low-level program that | > should be wrapped in a nicer UI. I'll send a couple of perl scripts | > later that make it easier to use. | -- "It is seldom that liberty of any kind is lost all at once." -Hume
Anonymous User wrote:
This program can be used by anonymous contributors to release partial information about their identity - they can show that they are someone from a list of PGP key holders, without revealing which member of the list they are. Maybe it can help in the recent controvery over the identity of anonymous posters. It's a fairly low-level program that should be wrapped in a nicer UI. I'll send a couple of perl scripts later that make it easier to use.
Hmm. So has anyone managed to get the signature to verify? Doesn't work for me! But perhaps things got mangled in the mail? Or I chose the wrong subset of the email to verify (I tried all the obvious ones)? Sending this stuff as attachments instead of inline would work better, of course. Cheers, Ben. -- http://www.apache-ssl.org/ben.html http://www.thebunker.net/ Available for contract work. "There is no limit to what a man can do or how far he can go if he doesn't mind who gets the credit." - Robert Woodruff
-----BEGIN PGP MESSAGE----- Version: GnuPG v1.0.7 Comment: No comment. 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 pCmfuaFH7VshcBugy+ZRyjBxgTTZGc/fTR2sz4w= =2IHX -----END PGP MESSAGE-----
-----BEGIN PGP MESSAGE----- Version: GnuPG v1.0.7 Comment: No comment. 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 =7Jh5 -----END PGP MESSAGE-----
participants (5)
-
Adam Back
-
Adam Shostack
-
Anonymous User
-
Ben Laurie
-
Meyer Wolfsheim