From 7b7aa6d395361bb8fec1400215c594a187cb3858 Mon Sep 17 00:00:00 2001 From: TLINDEN Date: Thu, 23 Jan 2014 23:36:57 +0100 Subject: [PATCH 1/3] added pcp_ed_verify_buffered() [doesnt work yet, needs debugging] --- include/pcp/defines.h | 7 ++- include/pcp/ed.h | 4 ++ include/pcp/util.h | 20 ++++++- libpcp/ed.c | 136 +++++++++++++++++++++++++++++++++++++++++- src/signature.c | 43 ++----------- 5 files changed, 166 insertions(+), 44 deletions(-) diff --git a/include/pcp/defines.h b/include/pcp/defines.h index 2510c31..fcbd155 100644 --- a/include/pcp/defines.h +++ b/include/pcp/defines.h @@ -40,9 +40,10 @@ typedef unsigned int qbyte; // Quad byte = 32 bits #define PCP_ZFILE_HEADER "----- BEGIN Z85 ENCODED FILE -----" #define PCP_ZFILE_FOOTER "------ END Z85 ENCODED FILE ------" -#define PCP_SIG_HEADER "----- BEGIN PCP SIGNED MESSAGE -----" -#define PCP_SIG_START "----- BEGIN PCP SIGNATURE -----" -#define PCP_SIG_END "------ END PCP SIGNATURE ------" +#define PCP_SIG_HEADER "----- BEGIN ED25519 SIGNED MESSAGE -----" +#define PCP_SIG_START "----- BEGIN ED25519 SIGNATURE -----" +#define PCP_SIG_END "------ END ED25519 SIGNATURE ------" +#define PCP_SIGPREFIX "\nnacl-" #define PCP_ME "Pretty Curved Privacy" diff --git a/include/pcp/ed.h b/include/pcp/ed.h index adeeb79..9e0bbe3 100644 --- a/include/pcp/ed.h +++ b/include/pcp/ed.h @@ -35,6 +35,8 @@ #include "platform.h" #include "mem.h" #include "key.h" +#include "keyhash.h" +#include "util.h" /* sign a message of messagesize using s->edsecret, if it works return message+signature (size: messagesize + crypto_sign_BYTES), @@ -51,4 +53,6 @@ unsigned char * pcp_ed_verify(unsigned char *signature, size_t siglen, pcp_pubke sig only to the output */ size_t pcp_ed_sign_buffered(FILE *in, FILE *out, pcp_key_t *s, int z85); +unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p); + #endif // _HAVE_PCP_ED_H diff --git a/include/pcp/util.h b/include/pcp/util.h index 82020c0..9746006 100644 --- a/include/pcp/util.h +++ b/include/pcp/util.h @@ -26,13 +26,31 @@ #define _HAVE_PCP_UTIL_H #include +#include +// lowercase a string static inline char *_lc(char *in) { size_t len = strlen(in); - int i; + size_t i; for(i=0; iedsecret); if(z85) { - if(in_buf[cur_bufsize] != '\n') - fprintf(out, "\n"); - fprintf(out, "%s\nVersion: PCP v%d.%d.%d\n\n", PCP_SIG_START, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); + fprintf(out, "\n%s\nVersion: PCP v%d.%d.%d\n\n", PCP_SIG_START, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); size_t zlen; char *z85encoded = pcp_z85_encode((unsigned char*)signature, crypto_sign_BYTES, &zlen); fprintf(out, "%s\n%s\n", z85encoded, PCP_SIG_END); @@ -99,3 +97,135 @@ size_t pcp_ed_sign_buffered(FILE *in, FILE *out, pcp_key_t *s, int z85) { return mlen; // ??? } + + +unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { + unsigned char in_buf[PCP_BLOCK_SIZE]; + size_t cur_bufsize = 0; + int z85 = 0; + crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state)); + unsigned char hash[crypto_generichash_BYTES_MAX]; + char *zhead; + size_t hlen = strlen(PCP_SIG_HEADER); + size_t nextsize = 0; + size_t restsize = 0; + size_t mlen = + crypto_sign_BYTES + crypto_generichash_BYTES_MAX; + unsigned char z85encoded[181]; + unsigned char sighash[mlen]; + unsigned char sig[crypto_sign_BYTES]; + char z85sigstart[] = PCP_SIG_START; + char binsigstart[] = PCP_SIGPREFIX; + int offset = -1; + + crypto_generichash_init(st, NULL, 0, 0); + + // determine sig type, clear or bin + cur_bufsize = hlen + 14; // header + hash name + 3x newline + fread(&in_buf, 1, cur_bufsize, in); + zhead = ucmalloc(cur_bufsize); + memcpy(zhead, in_buf, hlen); + memset(&zhead[hlen], 0, 1); + + if(strncmp(zhead, PCP_SIG_HEADER, hlen) == 0) + z85 = 1; + else + nextsize = cur_bufsize; + + while(!feof(in)) { + if(z85 == 0 && nextsize > 0) { + // bin sig, read blocksize - header and put zheader in front of it again + cur_bufsize = fread(&in_buf[hlen], 1, PCP_BLOCK_SIZE - hlen, in); + memcpy(in_buf, zhead, hlen); + cur_bufsize += hlen; + } + else + cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in); + + if(cur_bufsize <= 0) + break; + + if(z85) { + // look if we need to cut something from the current block + offset = _findoffset(in_buf, cur_bufsize, z85sigstart, strlen(z85sigstart)); + if(offset > 0) { + // we need to cut + restsize = cur_bufsize - offset; // the start of the armor sig + cur_bufsize -= restsize; // bin stuff in front of it, if any + memcpy(z85encoded, &in_buf[offset], restsize); // save the armor sig chunk + } + } + else { + // do the same for the bin sig + offset = _findoffset(in_buf, cur_bufsize, binsigstart, strlen(binsigstart)); + if(offset > 0) { + // we need to cut + restsize = cur_bufsize - offset + strlen(binsigstart); // the start of the bin sig + cur_bufsize -= offset; // bin stuff in front of it, if any + memcpy(sighash, &in_buf[offset + strlen(binsigstart)], restsize); // save the armor sig chunk + } + } + + if(offset == -1) + crypto_generichash_update(st, in_buf, cur_bufsize); + } + + if(offset == -1) { + fatal("Error, the signature doesn't contain the ed25519 signed hash\n"); + goto errvb1; + } + + crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX); + + // pull in the remainder + cur_bufsize = fread(&in_buf, 1, restsize, in); + + // put hashsig together + if(z85) { + memcpy(&z85encoded[restsize], in_buf, cur_bufsize); + char *z85block = pcp_readz85string(z85encoded, restsize + cur_bufsize); + if(z85block == NULL) + goto errvb1; + size_t dstlen; + unsigned char *z85decoded = pcp_z85_decode(z85block, &dstlen); + if(dstlen != mlen) { + fatal("z85 decoded signature didn't result in a proper signed hash\n"); + goto errvb1; + } + memcpy(sighash, z85decoded, mlen); + } + else { + memcpy(&sighash[restsize], in_buf, cur_bufsize); // FIXME: check if cur_bufsize holds enough + } + + // huh, how did we made it til here? + unsigned char *verifiedhash = NULL; + if(p == NULL) { + pcphash_iteratepub(p) { + verifiedhash = pcp_ed_verify(sighash, mlen, p); + if(verifiedhash != NULL) + break; + } + } + else { + verifiedhash = pcp_ed_verify(sighash, mlen, p); + } + + if(verifiedhash == NULL) + goto errvb1; + + + if(memcmp(verifiedhash, hash, crypto_generichash_BYTES_MAX) != 0) { + // sig verified, but the hash doesn't + fatal("signed hash doesn't match with actual hash of signed file content\n"); + free(verifiedhash); + } + + return verifiedhash; + + + errvb1: + free(st); + free(zhead); + return NULL; +} + diff --git a/src/signature.c b/src/signature.c index 6447767..ad99630 100644 --- a/src/signature.c +++ b/src/signature.c @@ -99,60 +99,29 @@ int pcpverify(char *infile, char *id) { if(id != NULL) HASH_FIND_STR(pcppubkey_hash, id, pub); - /* - if(pub == NULL) { - fatal("Could not find a usable public key in vault %s!\n", - vault->filename); - goto errv3; - } - */ - - unsigned char *input = NULL; - size_t inputBufSize = 0; - unsigned char byte[1]; - - while(!feof(in)) { - if(!fread(&byte, 1, 1, in)) - break; - unsigned char *tmp = realloc(input, inputBufSize + 1); - input = tmp; - memmove(&input[inputBufSize], byte, 1); - inputBufSize ++; - } - fclose(in); - - if(inputBufSize == 0) { - fatal("Input file is empty!\n"); - goto errv4; - } - if(pub != NULL) { - message = pcp_ed_verify(input, inputBufSize, pub); + message = pcp_ed_verify_buffered(in, pub); if(message != NULL) { fprintf(stderr, "Signature verified (signed by %s <%s>).\n", pub->owner, pub->mail); } } else { - pcphash_iteratepub(pub) { - message = pcp_ed_verify(input, inputBufSize, pub); - if(message != NULL) { - fprintf(stderr, "Signature verified (signed by %s <%s>).\n", pub->owner, pub->mail); - break; - } + // put public key as pub, so verify iterates over our keys + message = pcp_ed_verify_buffered(in, pub); + if(message != NULL) { + fprintf(stderr, "Signature verified (signed by %s <%s>).\n", pub->owner, pub->mail); } } if(message == NULL) { - fprintf(stderr, "Could not verify ignature\n"); + fprintf(stderr, "Could not verify signature\n"); } else free(message); - free(input); return 0; errv4: - free(input); errv1: return 1; From 52a7509fe2643335476e819ef8d86cc26447850a Mon Sep 17 00:00:00 2001 From: TLINDEN Date: Sun, 26 Jan 2014 11:57:23 +0100 Subject: [PATCH 2/3] sig calculation and output follows pbp scheme. unittests, doc and detached sigs still missing. --- ChangeLog | 19 ++--- TODO | 16 +--- include/pcp/util.h | 13 ++- libpcp/ed.c | 203 ++++++++++++++++++++++++++++----------------- src/signature.c | 10 +-- 5 files changed, 151 insertions(+), 110 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9e83b86..d8fc4dd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,17 +12,16 @@ Encrypted file format/scheme changed. Previously I included the sender's key-id with the encrypted - cipher as a hash. Now the sender's public key will - be included directly. This way I don't have to reveal - key-ids (which is bad) and people can encrypt for - others without a full key exchange first. + cipher as a hash. So, encrypted message do no more + contain pk material. + + Changed signature scheme completely. Binary signature + follow the pbp scheme: calculate blake2 hash of the + content, sign the hash, write out original content, + add "\nnacl-", add the signature, add the hash. + Armored signatures are calculated the same way but + output follows the pgp scheme instead. - Also I'm no more using the primary secret (or any - other secret in the vault) for encryption. Instead - every time a user encrypts a file, a new keypair - will be generated. That way the file can only be - decrypted by the recipient (which public key have - been used) and no one else, not even the sender. 0.1.5 Fixed a segmentation fault when using pcp1 -t on a public key. I added a double free() there by purpose diff --git a/TODO b/TODO index e8b38bf..5fae59f 100644 --- a/TODO +++ b/TODO @@ -10,19 +10,7 @@ key++: normalize id and lc() allow signing using an alternate secret key, like in pcpdecrypt() -use recipient in encryption to lookup public key in the vault, if id not given - -change encrypted file format to the one of pbp, following it: - - support multiple recipients - - encrypt 32k blockwise using crypto_secretbox() using - a random key, encrypt that key for each recipient with - pk using crypto_box() - - base85 <=> z85? maybe make us base85 tolerant while still - preferring z85? I dunno... - support export/import from/to pbp -remove key-id from stored signatures, maybe add the file content again -(by default a signature consists of the file+sig, but I sometime decided -to detach the sig and only write this to the signature file, at least pbp -stores the full stuff, so I shall do it as well). \ No newline at end of file +re-add detached signatures, calculated with the pbp scheme but with +separate files (as I had it before). \ No newline at end of file diff --git a/include/pcp/util.h b/include/pcp/util.h index 9746006..5f1b60c 100644 --- a/include/pcp/util.h +++ b/include/pcp/util.h @@ -38,18 +38,23 @@ static inline char *_lc(char *in) { } // find the offset of the beginning of a certain string within binary data -static inline int _findoffset(unsigned char *bin, size_t binlen, char *sigstart, size_t hlen) { +static inline size_t _findoffset(unsigned char *bin, size_t binlen, char *sigstart, size_t hlen) { size_t i; - int sigfoot = 181; // yes, also bad. that's the armored sig footer - int offset = -1; + size_t offset = 0; + int m = 0; - for(i=0; iedsecret); if(z85) { - fprintf(out, "\n%s\nVersion: PCP v%d.%d.%d\n\n", PCP_SIG_START, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); + fprintf(out, "\n%s\n Version: PCP v%d.%d.%d\n\n", PCP_SIG_START, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); size_t zlen; - char *z85encoded = pcp_z85_encode((unsigned char*)signature, crypto_sign_BYTES, &zlen); + char *z85encoded = pcp_z85_encode((unsigned char*)signature, mlen, &zlen); fprintf(out, "%s\n%s\n", z85encoded, PCP_SIG_END); } else { - fwrite(signature, crypto_sign_BYTES, 1, out); + fprintf(out, "%s", PCP_SIGPREFIX); + fwrite(signature, mlen, 1, out); } if(fileno(in) != 0) @@ -95,107 +96,158 @@ size_t pcp_ed_sign_buffered(FILE *in, FILE *out, pcp_key_t *s, int z85) { free(st); - return mlen; // ??? + return outsize; } - unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { - unsigned char in_buf[PCP_BLOCK_SIZE]; + unsigned char in_buf[PCP_BLOCK_SIZE/2]; + unsigned char in_next[PCP_BLOCK_SIZE/2]; + unsigned char in_full[PCP_BLOCK_SIZE]; + size_t cur_bufsize = 0; + size_t next_bufsize = 0; + size_t full_bufsize = 0; + int z85 = 0; - crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state)); + int gotsig = 0; + unsigned char hash[crypto_generichash_BYTES_MAX]; - char *zhead; + char zhead[] = PCP_SIG_HEADER; size_t hlen = strlen(PCP_SIG_HEADER); - size_t nextsize = 0; - size_t restsize = 0; + size_t hlen2 = 15; // hash: blake2\n\n size_t mlen = + crypto_sign_BYTES + crypto_generichash_BYTES_MAX; - unsigned char z85encoded[181]; + size_t zlen = 262; // FIXME: calculate + unsigned char z85encoded[zlen]; unsigned char sighash[mlen]; - unsigned char sig[crypto_sign_BYTES]; char z85sigstart[] = PCP_SIG_START; char binsigstart[] = PCP_SIGPREFIX; - int offset = -1; + char sigstart[] = PCP_SIG_START; + size_t siglen, startlen; + size_t offset = -1; + crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state)); crypto_generichash_init(st, NULL, 0, 0); - // determine sig type, clear or bin - cur_bufsize = hlen + 14; // header + hash name + 3x newline - fread(&in_buf, 1, cur_bufsize, in); - zhead = ucmalloc(cur_bufsize); - memcpy(zhead, in_buf, hlen); - memset(&zhead[hlen], 0, 1); + /* use two half blocks, to overcome sigs spanning block boundaries */ + cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE/2, in); - if(strncmp(zhead, PCP_SIG_HEADER, hlen) == 0) + // look for z85 header and cut it out + if(_findoffset(in_buf, cur_bufsize, zhead, hlen) == 0) { + // it is armored + next_bufsize = cur_bufsize - (hlen+hlen2); // size - the header + memcpy(in_next, &in_buf[hlen+hlen2], next_bufsize); // tmp save + memcpy(in_buf, in_next, next_bufsize); // put into inbuf without header + if(cur_bufsize == PCP_BLOCK_SIZE/2) { + // more to come + cur_bufsize = fread(&in_buf[next_bufsize], 1, ((PCP_BLOCK_SIZE/2) - next_bufsize), in); + cur_bufsize += next_bufsize; + next_bufsize = 0; + // now we've got the 1st half block in in_buf + // unless the file was smaller than blocksize/2, + // in which case it contains all the rest til eof + } z85 = 1; - else - nextsize = cur_bufsize; - - while(!feof(in)) { - if(z85 == 0 && nextsize > 0) { - // bin sig, read blocksize - header and put zheader in front of it again - cur_bufsize = fread(&in_buf[hlen], 1, PCP_BLOCK_SIZE - hlen, in); - memcpy(in_buf, zhead, hlen); - cur_bufsize += hlen; - } - else - cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in); - - if(cur_bufsize <= 0) - break; - - if(z85) { - // look if we need to cut something from the current block - offset = _findoffset(in_buf, cur_bufsize, z85sigstart, strlen(z85sigstart)); - if(offset > 0) { - // we need to cut - restsize = cur_bufsize - offset; // the start of the armor sig - cur_bufsize -= restsize; // bin stuff in front of it, if any - memcpy(z85encoded, &in_buf[offset], restsize); // save the armor sig chunk - } - } - else { - // do the same for the bin sig - offset = _findoffset(in_buf, cur_bufsize, binsigstart, strlen(binsigstart)); - if(offset > 0) { - // we need to cut - restsize = cur_bufsize - offset + strlen(binsigstart); // the start of the bin sig - cur_bufsize -= offset; // bin stuff in front of it, if any - memcpy(sighash, &in_buf[offset + strlen(binsigstart)], restsize); // save the armor sig chunk - } - } - - if(offset == -1) - crypto_generichash_update(st, in_buf, cur_bufsize); } - if(offset == -1) { + if(z85 == 1) { + siglen = zlen; + strcpy(sigstart, z85sigstart); + startlen = strlen(z85sigstart); + } + else { + siglen = mlen + strlen(binsigstart); + strcpy(sigstart, binsigstart); + startlen = strlen(binsigstart); + } + + + while (cur_bufsize > 0) { + if(cur_bufsize == PCP_BLOCK_SIZE/2) { + // probably not eof + next_bufsize = fread(&in_next, 1, PCP_BLOCK_SIZE/2, in); + } + else + next_bufsize = 0; // <= this is eof + + // concatenate previous and current buffer + if(next_bufsize == 0) + memcpy(in_full, in_buf, cur_bufsize); + else { + memcpy(in_full, in_buf, cur_bufsize); + memcpy(&in_full[cur_bufsize], in_next, next_bufsize); + } + full_bufsize = cur_bufsize+next_bufsize; + + // find signature offset + offset = _findoffset(in_full, full_bufsize, sigstart, startlen); + + //printf("offset: %ld, full: %ld, cur: %ld\n", offset, full_bufsize, cur_bufsize); + + if(offset >= 0 && offset <= PCP_BLOCK_SIZE/2) { + // sig begins within the first half, adjust in_buf size + //printf("1st half\n"); + next_bufsize = 0; + cur_bufsize = offset; + gotsig = 1; + if(z85) { + cur_bufsize -= 1; + memcpy(z85encoded, &in_full[offset], zlen); + } + else + memcpy(sighash, &in_full[offset + strlen(binsigstart)], mlen); + } + else if(full_bufsize - offset == siglen) { + // sig fits within the 2nd half + // offset: 28279, full: 28413, cur: 16384 + //printf("2nd half\n"); + next_bufsize -= siglen; + gotsig = 1; + if(z85) { + cur_bufsize -= 1; + memcpy(z85encoded, &in_full[full_bufsize - siglen], siglen); + } + else + memcpy(sighash, &in_full[full_bufsize - mlen], mlen); + } + else + offset = 0; + + // add previous half block to hash + crypto_generichash_update(st, in_buf, cur_bufsize); + + // next => in + if(next_bufsize > 0) { + memcpy(in_buf, in_next, next_bufsize); + cur_bufsize = next_bufsize; + } + else + break; + } // while + + if(gotsig == 0) { fatal("Error, the signature doesn't contain the ed25519 signed hash\n"); goto errvb1; } crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX); - // pull in the remainder - cur_bufsize = fread(&in_buf, 1, restsize, in); - - // put hashsig together if(z85) { - memcpy(&z85encoded[restsize], in_buf, cur_bufsize); - char *z85block = pcp_readz85string(z85encoded, restsize + cur_bufsize); + char *z85block = pcp_readz85string(z85encoded, zlen); if(z85block == NULL) goto errvb1; + + fprintf(stderr, "<%s>\n", z85block); + size_t dstlen; unsigned char *z85decoded = pcp_z85_decode(z85block, &dstlen); if(dstlen != mlen) { - fatal("z85 decoded signature didn't result in a proper signed hash\n"); + fatal("z85 decoded signature didn't result in a proper signed hash(got: %ld, expected: %ld)\n", dstlen, mlen); goto errvb1; } memcpy(sighash, z85decoded, mlen); } - else { - memcpy(&sighash[restsize], in_buf, cur_bufsize); // FIXME: check if cur_bufsize holds enough - } + // else: if unarmored, sighash is already filled + // huh, how did we made it til here? unsigned char *verifiedhash = NULL; @@ -213,11 +265,11 @@ unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { if(verifiedhash == NULL) goto errvb1; - if(memcmp(verifiedhash, hash, crypto_generichash_BYTES_MAX) != 0) { // sig verified, but the hash doesn't - fatal("signed hash doesn't match with actual hash of signed file content\n"); + fatal("signed hash doesn't match actual hash of signed file content\n"); free(verifiedhash); + return NULL; } return verifiedhash; @@ -225,7 +277,6 @@ unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { errvb1: free(st); - free(zhead); return NULL; } diff --git a/src/signature.c b/src/signature.c index ad99630..8b5febf 100644 --- a/src/signature.c +++ b/src/signature.c @@ -29,6 +29,7 @@ int pcpsign(char *infile, char *outfile, char *passwd, int z85) { pcp_key_t *secret = NULL; secret = pcp_find_primary_secret(); + if(secret == NULL) { fatal("Could not find a secret key in vault %s!\n", vault->filename); goto errs1; @@ -113,13 +114,10 @@ int pcpverify(char *infile, char *id) { } } - if(message == NULL) { - fprintf(stderr, "Could not verify signature\n"); - } - else + if(message != NULL) { free(message); - - return 0; + return 0; + } errv4: From 78987a1d27bf500c322159e50092a5cc720a26cd Mon Sep 17 00:00:00 2001 From: TLINDEN Date: Sun, 26 Jan 2014 16:02:47 +0100 Subject: [PATCH 3/3] (re-)added detached signature support, now with 32k-blockwise reading of files, enabled with -a. --- ChangeLog | 6 ++ include/pcp/ed.h | 5 +- libpcp/crypto.c | 2 - libpcp/ed.c | 142 ++++++++++++++++++++++++++++++++++++++++++-- src/encryption.c | 2 +- src/pcp.c | 20 +++++-- src/signature.c | 44 +++++++------- src/signature.h | 4 +- src/usage.txt | 6 +- tests/unittests.cfg | 29 +++++++-- 10 files changed, 218 insertions(+), 42 deletions(-) diff --git a/ChangeLog b/ChangeLog index d8fc4dd..76d00ef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,6 +22,12 @@ Armored signatures are calculated the same way but output follows the pgp scheme instead. + Detached signatures are still supported as before, + for the user everything with them is as known, but + the commandline option -a (--detach) have to be + applied. Internally, however, inputs will be read in + 32k blockwise as well. Detached signatures are now + z85 encoded always. 0.1.5 Fixed a segmentation fault when using pcp1 -t on a public key. I added a double free() there by purpose diff --git a/include/pcp/ed.h b/include/pcp/ed.h index 9e0bbe3..4ed7975 100644 --- a/include/pcp/ed.h +++ b/include/pcp/ed.h @@ -53,6 +53,9 @@ unsigned char * pcp_ed_verify(unsigned char *signature, size_t siglen, pcp_pubke sig only to the output */ size_t pcp_ed_sign_buffered(FILE *in, FILE *out, pcp_key_t *s, int z85); -unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p); +pcp_pubkey_t *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p); + +size_t pcp_ed_detachsign_buffered(FILE *in, FILE *out, pcp_key_t *s); +pcp_pubkey_t *pcp_ed_detachverify_buffered(FILE *in, FILE *sigfd, pcp_pubkey_t *p); #endif // _HAVE_PCP_ED_H diff --git a/libpcp/crypto.c b/libpcp/crypto.c index 05063c9..ac63c71 100644 --- a/libpcp/crypto.c +++ b/libpcp/crypto.c @@ -456,6 +456,4 @@ size_t pcp_decrypt_file_sym(FILE *in, FILE* out, unsigned char *symkey) { fclose(out); return out_size; - - } diff --git a/libpcp/ed.c b/libpcp/ed.c index 57f0093..b040613 100644 --- a/libpcp/ed.c +++ b/libpcp/ed.c @@ -99,7 +99,7 @@ size_t pcp_ed_sign_buffered(FILE *in, FILE *out, pcp_key_t *s, int z85) { return outsize; } -unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { +pcp_pubkey_t *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { unsigned char in_buf[PCP_BLOCK_SIZE/2]; unsigned char in_next[PCP_BLOCK_SIZE/2]; unsigned char in_full[PCP_BLOCK_SIZE]; @@ -236,8 +236,6 @@ unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { if(z85block == NULL) goto errvb1; - fprintf(stderr, "<%s>\n", z85block); - size_t dstlen; unsigned char *z85decoded = pcp_z85_decode(z85block, &dstlen); if(dstlen != mlen) { @@ -272,7 +270,7 @@ unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { return NULL; } - return verifiedhash; + return p; errvb1: @@ -280,3 +278,139 @@ unsigned char *pcp_ed_verify_buffered(FILE *in, pcp_pubkey_t *p) { return NULL; } +size_t pcp_ed_detachsign_buffered(FILE *in, FILE *out, pcp_key_t *s) { + unsigned char in_buf[PCP_BLOCK_SIZE]; + size_t cur_bufsize = 0; + size_t outsize = 0; + crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state)); + unsigned char hash[crypto_generichash_BYTES_MAX]; + + crypto_generichash_init(st, NULL, 0, 0); + + while(!feof(in)) { + cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in); + if(cur_bufsize <= 0) + break; + outsize += cur_bufsize; + crypto_generichash_update(st, in_buf, cur_bufsize); + } + + crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX); + + unsigned char *signature = pcp_ed_sign(hash, crypto_generichash_BYTES_MAX, s); + size_t mlen = + crypto_sign_BYTES + crypto_generichash_BYTES_MAX; + + fprintf(out, "\n%s\n Version: PCP v%d.%d.%d\n\n", + PCP_SIG_START, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); + size_t zlen; + char *z85encoded = pcp_z85_encode((unsigned char*)signature, mlen, &zlen); + fprintf(out, "%s\n%s\n", z85encoded, PCP_SIG_END); + + if(fileno(in) != 0) + fclose(in); + if(fileno(out) != 1) + fclose(out); + + free(st); + + return outsize; +} + +pcp_pubkey_t *pcp_ed_detachverify_buffered(FILE *in, FILE *sigfd, pcp_pubkey_t *p) { + unsigned char in_buf[PCP_BLOCK_SIZE]; + size_t cur_bufsize = 0; + size_t outsize = 0; + crypto_generichash_state *st = ucmalloc(sizeof(crypto_generichash_state)); + unsigned char hash[crypto_generichash_BYTES_MAX]; + size_t mlen = + crypto_sign_BYTES + crypto_generichash_BYTES_MAX; + + crypto_generichash_init(st, NULL, 0, 0); + + while(!feof(in)) { + cur_bufsize = fread(&in_buf, 1, PCP_BLOCK_SIZE, in); + if(cur_bufsize <= 0) + break; + outsize += cur_bufsize; + crypto_generichash_update(st, in_buf, cur_bufsize); + } + + crypto_generichash_final(st, hash, crypto_generichash_BYTES_MAX); + + // read the sig + unsigned char *sig = NULL; + size_t inputBufSize = 0; + unsigned char byte[1]; + + while(!feof(sigfd)) { + if(!fread(&byte, 1, 1, sigfd)) + break; + unsigned char *tmp = realloc(sig, inputBufSize + 1); + sig = tmp; + memmove(&sig[inputBufSize], byte, 1); + inputBufSize ++; + } + fclose(sigfd); + + if(sig == NULL) { + fatal("Invalid detached signature\n"); + goto errdea1; + } + + + char *z85block = pcp_readz85string(sig, inputBufSize); + if(z85block == NULL) + goto errdea2; + + size_t clen; + unsigned char *sighash = pcp_z85_decode(z85block, &clen); + if(sighash == NULL) + goto errdea3; + + if(clen != mlen) { + fatal("z85 decoded signature didn't result in a proper signed hash(got: %ld, expected: %ld)\n", clen, mlen); + goto errdea4; + } + + unsigned char *verifiedhash = NULL; + if(p == NULL) { + pcphash_iteratepub(p) { + verifiedhash = pcp_ed_verify(sighash, mlen, p); + if(verifiedhash != NULL) + break; + } + } + else { + verifiedhash = pcp_ed_verify(sighash, mlen, p); + } + + if(verifiedhash == NULL) + goto errdea4; + + if(memcmp(verifiedhash, hash, crypto_generichash_BYTES_MAX) != 0) { + // sig verified, but the hash doesn't + fatal("signed hash doesn't match actual hash of signed file content\n"); + goto errdea5; + } + + free(verifiedhash); + free(sighash); + free(z85block); + free(sig); + return p; + + + errdea5: + free(verifiedhash); + + errdea4: + free(sighash); + + errdea3: + free(z85block); + + errdea2: + free(sig); + + errdea1: + return NULL; +} diff --git a/src/encryption.c b/src/encryption.c index 6bf5cf3..71a0bb0 100644 --- a/src/encryption.c +++ b/src/encryption.c @@ -259,7 +259,7 @@ int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *rec size_t clen = 0; if(self == 1) - pcp_encrypt_file_sym(in, out, symkey, 0); + clen = pcp_encrypt_file_sym(in, out, symkey, 0); else clen = pcp_encrypt_file(in, out, secret, pubhash); diff --git a/src/pcp.c b/src/pcp.c index c2dafb5..021dad3 100644 --- a/src/pcp.c +++ b/src/pcp.c @@ -44,10 +44,11 @@ char *default_vault() { } int main (int argc, char **argv) { - int opt, mode, usevault, useid, userec, lo, armor; + int opt, mode, usevault, useid, userec, lo, armor, detach; char *vaultfile = default_vault(); char *outfile = NULL; char *infile = NULL; + char *sigfile = NULL; char *keyid = NULL; char *id = NULL; char *xpass = NULL; @@ -63,6 +64,7 @@ int main (int argc, char **argv) { userec = 0; lo = 0; armor = 0; + detach = 0; static struct option longopts[] = { // generics @@ -101,11 +103,12 @@ int main (int argc, char **argv) { // signing { "sign", no_argument, NULL, 'g' }, - { "check-signature", no_argument, NULL, 'c' }, + { "check-signature", required_argument, NULL, 'c' }, + { "detach", no_argument, NULL, 'a' }, { NULL, 0, NULL, 0 } }; - while ((opt = getopt_long(argc, argv, "klV:vdehsO:i:I:pSPRtEx:DzZr:gcym", + while ((opt = getopt_long(argc, argv, "klV:vdehsO:i:I:pSPRtEx:DzZr:gc:yma", longopts, NULL)) != -1) { switch (opt) { @@ -171,12 +174,17 @@ int main (int argc, char **argv) { case 'Z': armor = 1; break; + case 'a': + detach = 1; + break; case 'g': mode += PCP_MODE_SIGN; usevault = 1; break; case 'c': mode += PCP_MODE_VERIFY; + sigfile = ucmalloc(strlen(optarg)+1); + strncpy(sigfile, optarg, strlen(optarg)+1); usevault = 1; break; case 'y': @@ -372,19 +380,19 @@ int main (int argc, char **argv) { break; case PCP_MODE_SIGN: - pcpsign(infile, outfile, xpass, armor); + pcpsign(infile, outfile, xpass, armor, detach); break; case PCP_MODE_VERIFY: if(useid) { id = pcp_normalize_id(keyid); if(id != NULL) { - pcpverify(infile, id); + pcpverify(infile, sigfile, id, detach); free(id); } } else { - pcpverify(infile, NULL); + pcpverify(infile, sigfile, NULL, detach); } break; diff --git a/src/signature.c b/src/signature.c index 8b5febf..27b8fd1 100644 --- a/src/signature.c +++ b/src/signature.c @@ -23,7 +23,8 @@ #include "signature.h" #include "defines.h" -int pcpsign(char *infile, char *outfile, char *passwd, int z85) { + +int pcpsign(char *infile, char *outfile, char *passwd, int z85, int detach) { FILE *in = NULL; FILE *out = NULL; pcp_key_t *secret = NULL; @@ -70,7 +71,11 @@ int pcpsign(char *infile, char *outfile, char *passwd, int z85) { goto errs1; } - size_t sigsize = pcp_ed_sign_buffered(in, out, secret, z85); + size_t sigsize; + if(detach == 1) + sigsize = pcp_ed_detachsign_buffered(in, out, secret); + else + sigsize = pcp_ed_sign_buffered(in, out, secret, z85); if(sigsize == 0) goto errs1; @@ -83,10 +88,10 @@ int pcpsign(char *infile, char *outfile, char *passwd, int z85) { return 1; } -int pcpverify(char *infile, char *id) { +int pcpverify(char *infile, char *sigfile, char *id, int detach) { FILE *in = NULL; + FILE *sigfd = NULL; pcp_pubkey_t *pub = NULL; - unsigned char *message = NULL; if(infile == NULL) in = stdin; @@ -97,27 +102,24 @@ int pcpverify(char *infile, char *id) { } } + if(sigfile != NULL) { + if((sigfd = fopen(sigfile, "rb")) == NULL) { + fatal("Could not open signature file %s\n", sigfile); + goto errv1; + } + } + if(id != NULL) HASH_FIND_STR(pcppubkey_hash, id, pub); - if(pub != NULL) { - message = pcp_ed_verify_buffered(in, pub); - if(message != NULL) { - fprintf(stderr, "Signature verified (signed by %s <%s>).\n", pub->owner, pub->mail); - } - } - else { - // put public key as pub, so verify iterates over our keys - message = pcp_ed_verify_buffered(in, pub); - if(message != NULL) { - fprintf(stderr, "Signature verified (signed by %s <%s>).\n", pub->owner, pub->mail); - } - } + if(detach) + pub = pcp_ed_detachverify_buffered(in, sigfd, pub); + else + pub = pcp_ed_verify_buffered(sigfd, pub); - if(message != NULL) { - free(message); - return 0; - } + if(pub != NULL) + fprintf(stderr, "Signature verified (signed by %s <%s>).\n", pub->owner, pub->mail); + errv4: diff --git a/src/signature.h b/src/signature.h index 1ba3c12..d8b9919 100644 --- a/src/signature.h +++ b/src/signature.h @@ -32,8 +32,8 @@ #include "uthash.h" #include "z85.h" -int pcpsign(char *infile, char *outfile, char *passwd, int z85); -int pcpverify(char *infile, char *id); +int pcpsign(char *infile, char *outfile, char *passwd, int z85, int detach); +int pcpverify(char *infile, char *sigfile, char *id, int detach); diff --git a/src/usage.txt b/src/usage.txt index 5321b0c..5d9f977 100644 --- a/src/usage.txt +++ b/src/usage.txt @@ -92,11 +92,15 @@ Signature Options: -I (or from stdin) using your primary secret key. If -r has been given, a derived secret key will be used for signing. - -c --check-signature Verify a signature in file against the file specified with -I (or stdin). The public key required for this must exist in your vault file. +-a --detach Write a detached signature file, which doesn't + contain the original content. Output will be + z85 encoded always. To verify, you need to + specify the original file to be verified + against using -I as well (plus -a). Encoding Options: -z --z85-encode Encode something to Z85 encoding. Use diff --git a/tests/unittests.cfg b/tests/unittests.cfg index 911c395..e3c5842 100644 --- a/tests/unittests.cfg +++ b/tests/unittests.cfg @@ -210,13 +210,31 @@ dxmorg@florida.cops.gov # -# signature test - +# signature tests + + cmd = $pcp -V va -g -I README -O testsig -x a -a + expect-file testsig + + + cmd = $pcp -V vb -c testsig -I README -i $idalicia -a + expect = /verified/ + + + prepare = rm -f testsig + cmd = $pcp -V va -g -I README -O testsig -x a -z + expect-file testsig + + + cmd = $pcp -V vb -c testsig -i $idalicia -z + expect = /verified/ + + + prepare = rm -f testsig cmd = $pcp -V va -g -I README -O testsig -x a expect-file testsig - - cmd = $pcp -V vb -c -I testsig -i $idalicia + + cmd = $pcp -V vb -c testsig -i $idalicia expect = /verified/ @@ -332,12 +350,15 @@ dxmorg@florida.cops.gov */ +/* + * Disabled, since z85 standalone mode currently disabled prepare = ./jot 30 | while read ignore; do echo XXXXX; done \ | $pcp -z > testfile-nokey cmd = $pcp -V $vault -P -I testfile-nokey expect = /result to a proper sized key/ +*/ cmd = $pcp -V $vault -S -I bart.pub