diff --git a/ChangeLog b/ChangeLog index 7e39ba8..24d5f8d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -50,6 +50,9 @@ on the caller if it works on files or on buffers. Pcpstreams also automatically encode/decode Z85. + Due to the new pcpstream class pcp now supports + armored encrypted files, which it didn't previously. + Lots of refactoring have been done to clear things out and make the system work with the changes above. @@ -73,6 +76,11 @@ in man/html/. Latest API docs can be found on http://www.daemon.de/libpcp/. + At this point I'd like to thank Liquid Soul. Only + thanks to their music I was able to do those heavy + changes. It's like a drug boosting the brain. Love + U, man! + 0.2.0 ED25519 and Curve25519 keys are now generated separately (previously they were generated from one random seed, the curve had been derived from diff --git a/TODO b/TODO index ce4759c..da6b7de 100644 --- a/TODO +++ b/TODO @@ -46,6 +46,7 @@ Check is_utf8 license. Vault checksum with global vault +Symmetric crypt mode tries to open vault Python binding, e.g.: py % cdll.LoadLibrary("libsodium.so.8") diff --git a/include/pcp.h b/include/pcp.h index fb0d6c6..8b16eae 100644 --- a/include/pcp.h +++ b/include/pcp.h @@ -8,7 +8,6 @@ extern "C" { #include "pcp/config.h" #include "pcp/base85.h" #include "pcp/buffer.h" -#include "pcp/config.h" #include "pcp/crypto.h" #include "pcp/defines.h" #include "pcp/digital_crc32.h" diff --git a/include/pcp/defines.h b/include/pcp/defines.h index 1e336bd..c2f8e7f 100644 --- a/include/pcp/defines.h +++ b/include/pcp/defines.h @@ -1,7 +1,7 @@ /* This file is part of Pretty Curved Privacy (pcp1). - Copyright (C) 2013 T.Linden. + Copyright (C) 2013-2014 T.v.Dein. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - You can contact me by mail: . + You can contact me by mail: . */ @@ -58,8 +58,8 @@ typedef unsigned short dbyte; /* Double byte = 16 bits */ typedef unsigned int qbyte; /* Quad byte = 32 bits */ /* key stuff, deprecated. */ -#define PCP_ENFILE_HEADER "~~~~~ BEGIN PCP ENCRYPTED FILE ~~~~~" -#define PCP_ENFILE_FOOTER "~~~~~ END PCP ENCRYPTED FILE ~~~~~" +#define PCP_ENFILE_HEADER "~~~~~ BEGIN PCP ENCRYPTED FILE ~~~~~\r\n" +#define PCP_ENFILE_FOOTER "\r\n~~~~~ END PCP ENCRYPTED FILE ~~~~~\r\n" #define PCP_ZFILE_HEADER "~~~~~ BEGIN Z85 ENCODED FILE ~~~~~" #define PCP_ZFILE_FOOTER "~~~~~ END Z85 ENCODED FILE ~~~~~" diff --git a/include/pcp/pcpstream.h b/include/pcp/pcpstream.h index 65e9832..1c03a9e 100644 --- a/include/pcp/pcpstream.h +++ b/include/pcp/pcpstream.h @@ -70,6 +70,8 @@ struct _pcp_stream_t { uint8_t firstread; /**< Internal flag, will be set after first read() */ size_t linewr; /**< Used for Z85 writing, number of chars written on last line */ size_t blocksize; /**< Blocksize used for z85, if requested */ + uint8_t is_output; /**< marks the stream as output stream */ + size_t pos; /**< remember i/o position */ }; /** The name used everywhere */ @@ -254,6 +256,15 @@ void ps_setdetermine(Pcpstream *stream, size_t blocksize); */ void ps_armor(Pcpstream *stream, size_t blocksize); + +/** Disable Z85 encoding for an output stream. + + \param[in] stream The stream object. + */ +void ps_unarmor(Pcpstream *stream); + + + /* read from primary source, decode z85 and out into cache. if buf != NULL, consider it as the start of encoded data and remove headers and comments, then continue as normal. */ @@ -280,6 +291,9 @@ void ps_write_encode(Pcpstream *stream, Buffer *dst); /* really write the buffer z into the output stream */ size_t ps_write_buf(Pcpstream *stream, Buffer *z); +/* tell if we really reached eof, caching or not. 1=eof, 0=ok */ +int ps_left(Pcpstream *stream); + #endif // HAVE_PCP_PCPSTEAM_H diff --git a/include/pcp/version.h b/include/pcp/version.h index ae152e9..b3a672d 100644 --- a/include/pcp/version.h +++ b/include/pcp/version.h @@ -1,7 +1,7 @@ /* This file is part of Pretty Curved Privacy (pcp1). - Copyright (C) 2013 T.Linden. + Copyright (C) 2013-2014 T.v.Dein. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - You can contact me by mail: . + You can contact me by mail: . */ @@ -25,7 +25,7 @@ #define PCP_VERSION_MAJOR 0 #define PCP_VERSION_MINOR 2 -#define PCP_VERSION_PATCH 1 +#define PCP_VERSION_PATCH 2 #define PCP_MAKE_VERSION(major, minor, patch) \ ((major) * 10000 + (minor) * 100 + (patch)) diff --git a/libpcp/buffer.c b/libpcp/buffer.c index 4b5510a..5cae71e 100644 --- a/libpcp/buffer.c +++ b/libpcp/buffer.c @@ -125,11 +125,16 @@ byte *buffer_get(Buffer *b) { } size_t buffer_get_chunk(Buffer *b, void *buf, size_t len) { - if(len > b->end - b->offset || len == 0) { + if(len > b->end - b->offset) { fatal("[buffer %s] attempt to read %ld bytes data from buffer with %ld bytes left at offset %ld\n", b->name, len, b->end - b->offset, b->offset); return 0; } + else if(len == 0) { + /* FIXME: check how this happens */ + return 0; + } + memcpy(buf, b->buf + b->offset, len); b->offset += len; @@ -137,11 +142,15 @@ size_t buffer_get_chunk(Buffer *b, void *buf, size_t len) { } size_t buffer_get_chunk_tobuf(Buffer *b, Buffer *dst, size_t len) { - if(len > b->end - b->offset || len == 0) { + if(len > b->end - b->offset) { fatal("[buffer %s] attempt to read %ld bytes data from buffer with %ld bytes left at offset %ld\n", b->name, len, b->end - b->offset, b->offset); return 0; } + else if(len == 0) { + /* FIXME: check how this happens */ + return 0; + } buffer_resize(dst, len); memcpy(dst->buf+buffer_size(dst), b->buf + b->offset, len); diff --git a/libpcp/crypto.c b/libpcp/crypto.c index a928d1a..ec66b4f 100644 --- a/libpcp/crypto.c +++ b/libpcp/crypto.c @@ -178,7 +178,7 @@ size_t pcp_decrypt_stream(Pcpstream *in, Pcpstream* out, pcp_key_t *s, byte *sym nrec = recmatch = self = 0; - if(ps_tell(in) == 1) { + if(ps_tell(in) > 1) { /* header has already been determined outside the lib */ if(symkey != NULL) self = 1; @@ -233,7 +233,7 @@ size_t pcp_decrypt_stream(Pcpstream *in, Pcpstream* out, pcp_key_t *s, byte *sym for(nrec=0; nrecdetermine = 1; stream->blocksize = blocksize + (5 - (blocksize % 5)); if(stream->cache == NULL) { - stream->cache = buffer_new(32, "Pcpstreamcache"); - stream->next = buffer_new(32, "Pcpstreamcachenext"); + stream->cache = buffer_new(32, "Pcpstreamcachedetermine"); + stream->next = buffer_new(32, "Pcpstreamcachenextdetermin"); } } @@ -75,6 +75,10 @@ void ps_armor(Pcpstream *stream, size_t blocksize) { } } +void ps_unarmor(Pcpstream *stream) { + stream->armor = 0; +} + size_t ps_read_raw(Pcpstream *stream, void *buf, size_t readbytes) { size_t gotbytes = 0; @@ -107,18 +111,28 @@ size_t ps_read_raw(Pcpstream *stream, void *buf, size_t readbytes) { /* return readbytes from cache. if it is more than left in the cache fetch (and decode) the next chunk, append it to cache and return from that */ + size_t ps_read_cached(Pcpstream *stream, void *buf, size_t readbytes) { - if(buffer_left(stream->cache) <= readbytes && buffer_left(stream->cache) > 0 && readbytes <= stream->blocksize) { + /* + fprintf(stderr, "%ld <= %ld && %ld <= %ld\n", + readbytes, buffer_left(stream->cache), readbytes, stream->blocksize) ; + + fprintf(stderr, "%d == 1 && %ld >= %ld\n", ps_left(stream), readbytes, buffer_left(stream->cache)); + */ + if(readbytes <= buffer_left(stream->cache) && readbytes <= stream->blocksize) { /* enough left in current cache */ return buffer_get_chunk(stream->cache, buf, readbytes); } + else if(ps_left(stream) == 1 && readbytes >= buffer_left(stream->cache)) { + return buffer_get_chunk(stream->cache, buf, buffer_left(stream->cache)); + } else { /* request for chunk larger than what we've got in the cache */ - Buffer *tmp = buffer_new(stream->blocksize, "Pcpreadover"); + Buffer *tmp = buffer_new(stream->blocksize, "Pcpreadchunktmp"); if( buffer_left(stream->cache) > 0) { /* put the remaining cache into dest */ - buffer_get_chunk_tobuf(stream->cache, tmp, buffer_size(stream->cache)); + buffer_get_chunk_tobuf(stream->cache, tmp, buffer_left(stream->cache)); } /* how much left to fetch */ @@ -170,26 +184,31 @@ size_t ps_read_cached(Pcpstream *stream, void *buf, size_t readbytes) { /* read and decode the next chunk and put it into stream->next */ size_t ps_read_next(Pcpstream *stream) { - if(stream->armor == 1) { - /* fetch next chunk and decode it */ - return ps_read_decode(stream, NULL, 0); - } - else { - /* unencoded source, fetch as is */ - void *buf = ucmalloc(stream->blocksize); - size_t got = ps_read_raw(stream, buf, stream->blocksize); - buffer_add(stream->next, buf, got); - return got; + if(ps_left(stream) == 0) { + if(stream->armor == 1) { + /* fetch next chunk and decode it */ + return ps_read_decode(stream, NULL, 0); + } + else { + /* unencoded source, fetch as is */ + void *buf = ucmalloc(stream->blocksize); + size_t got = ps_read_raw(stream, buf, stream->blocksize); + buffer_add(stream->next, buf, got); + return got; + } } + else + return 0; } size_t ps_read(Pcpstream *stream, void *buf, size_t readbytes) { + size_t got = 0; if(stream->cache == NULL) { - return ps_read_raw(stream, buf, readbytes); + got = ps_read_raw(stream, buf, readbytes); } else if(buffer_size(stream->cache) > 0) { /* use cache */ - return ps_read_cached(stream, buf, readbytes); + got = ps_read_cached(stream, buf, readbytes); } else { /* no cache yet */ @@ -197,18 +216,22 @@ size_t ps_read(Pcpstream *stream, void *buf, size_t readbytes) { /* fetch the first chunk into the cache and decode, if required, recursively call ps_read() again to return the apropriate data */ ps_determine(stream); - return ps_read(stream, buf, readbytes); + got = ps_read(stream, buf, readbytes); } else if(stream->armor == 1) { /* z85 encoding has already been determined, therefore the cache is now filled, use it then */ - return ps_read_cached(stream, buf, readbytes); + got = ps_read_cached(stream, buf, readbytes); } else { /* read directly from source */ - return ps_read_raw(stream, buf, readbytes); + got = ps_read_raw(stream, buf, readbytes); } } + + stream->pos += got; + + return got; } void ps_determine(Pcpstream *stream) { @@ -285,6 +308,8 @@ size_t ps_read_decode(Pcpstream *stream, void *buf, size_t bufsize) { size_t ps_write(Pcpstream *stream, void *buf, size_t writebytes) { Buffer *z = buffer_new(32, "Pcpwritetemp"); + stream->is_output = 1; + if(stream->armor == 1) { if(buffer_size(stream->cache) + writebytes < stream->blocksize) { /* just put it into the cache and done with it */ @@ -350,13 +375,16 @@ size_t ps_write(Pcpstream *stream, void *buf, size_t writebytes) { /* actually put it out */ size_t outsize = ps_write_buf(stream, z); buffer_free(z); - return outsize; + writebytes = outsize; } else { /* buf has been put into the cache only, no writing required */ buffer_free(z); - return writebytes; } + + stream->pos += writebytes; + + return writebytes; } void ps_write_encode(Pcpstream *stream, Buffer *dst) { @@ -372,14 +400,14 @@ void ps_write_encode(Pcpstream *stream, Buffer *dst) { } /* z85 encode */ - zlen = (buffer_size(stream->cache) * 5 / 4); + zlen = (buffer_size(stream->cache) * 5 / 4) + 1; char *z85 = ucmalloc(zlen); zmq_z85_encode(z85, buffer_get(stream->cache), buffer_size(stream->cache)); /* add newlines */ pos = stream->linewr; - for(i=0; i= 71) { buffer_add8(dst, '\r'); buffer_add8(dst, '\n'); @@ -445,7 +473,11 @@ size_t ps_print(Pcpstream *stream, const char * fmt, ...) { void ps_close(Pcpstream *stream) { if(stream->cache != NULL) { - assert(buffer_left(stream->cache) == 0); /* there's something left in the cache, call ps_finish() */ + if(stream->is_output == 1) { + if(buffer_left(stream->cache) != 0) + buffer_info(stream->cache); + assert(buffer_left(stream->cache) == 0); /* there's something left in the cache, call ps_finish() */ + } buffer_free(stream->cache); } @@ -467,25 +499,31 @@ void ps_close(Pcpstream *stream) { int ps_end(Pcpstream *stream) { /* simulate open file if there's still something in the cache */ if(stream->cache != NULL) - if(buffer_left(stream->cache) > 0) + if(buffer_left(stream->cache) > 0) { return 0; + } return stream->eof; } +int ps_left(Pcpstream *stream) { + /* used internally to determine if we reached end of source */ + if(stream->is_buffer) { + if(buffer_left(stream->b) == 0) + return 1; + else + return 0; + } + else { + return feof(stream->fd); + } +} + int ps_err(Pcpstream *stream) { return stream->err; } size_t ps_tell(Pcpstream *stream) { - if(stream->is_buffer) { - if(stream->b->end > stream->b->offset) - return stream->b->end; /* write buffer */ - else - return stream->b->offset; /* read buffer */ - } - else { - return ftell(stream->fd); - } + return stream->pos; } Buffer *ps_buffer(Pcpstream *stream) { diff --git a/man/details.pod b/man/details.pod index fc6c77b..58f19cc 100644 --- a/man/details.pod +++ b/man/details.pod @@ -448,6 +448,9 @@ secret key, R is the recipient list, L is the number of recipients, T is the filetype header, I is a block of input with a size of 32k, N is a nonce (new per block) and S the symmetric key. +The encrypted output maybe Z85 encoded. In this case the Z85 +encoding will be done blockwise with blocks of 16k bytes. The +decoded content inside will be as described above. =head2 SIGNATURE FORMAT @@ -552,6 +555,19 @@ secret signing key and S the symmetric key. =head2 Z85 ENCODING B uses Z85 to encode exported keys and armored signatures. +Comments in encoded files are surrounded by the tilde character. +We're using the tilde because it's not part of the Z85 base +charset. Sample: + + ~~~ Header ~~~ + ~ Version: 1 ~ + 246ge]+yn={}]Xi3*N3Xx34Y^0rz:r.5j + v#6Sh/m3XKwy?VlA+h8ks]9:kVj{D[fd7]NA]T-(ne+xo!W5X5-gIUWqM + ~~~ Footer ~~~ + +Multiple tildes can be used as long as their number is uneven. + +This is a proprietary PCP extension. =head3 Z85 BACKGROUND diff --git a/man/options.pod b/man/options.pod index d8b941b..10688ce 100644 --- a/man/options.pod +++ b/man/options.pod @@ -75,6 +75,7 @@ If none of -i or -r has been given, encrypt the message symetrically. This is the same as -m (self-encryption mode). + Add -z to ascii armor the output using Z85. -m --encrypt-me Sym-Encrypt a message. Specify -I and/or -O for input/output file. You will be asked for a passphrase. No key material will @@ -112,9 +113,9 @@ If used with encryption or singing operation encode its output. Otherwise encode a plain file. Use -I and -O respectively, otherwise it - stdin/stdout. + uses stdin/stdout. -Z --z85-decode Decode (dearmor) something from Z85 encoding. Use -I and -O respectively, otherwise it - stdin/stdout + uses stdin/stdout diff --git a/src/encryption.c b/src/encryption.c index 070c5ae..ad13695 100644 --- a/src/encryption.c +++ b/src/encryption.c @@ -1,7 +1,7 @@ /* This file is part of Pretty Curved Privacy (pcp1). - Copyright (C) 2013 T.Linden. + Copyright (C) 2013-2014 T.v.Dein. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - You can contact me by mail: . + You can contact me by mail: . */ @@ -48,9 +48,15 @@ int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, i } } + Pcpstream *pin = ps_new_file(in); + Pcpstream *pout = ps_new_file(out); + + ps_setdetermine(pin, PCP_BLOCK_SIZE/2); + /* determine crypt mode */ - fread(&head, 1, 1, in); - if(!feof(in) && !ferror(in)) { + ps_read(pin, &head, 1); + + if(!ps_end(pin) && !ps_err(pin)) { if(head == PCP_SYM_CIPHER) { /* symetric mode */ byte *salt = ucmalloc(90); @@ -110,9 +116,6 @@ int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, i goto errde3; } - Pcpstream *pin = ps_new_file(in); - Pcpstream *pout = ps_new_file(out); - if(symkey == NULL) dlen = pcp_decrypt_stream(pin, pout, secret, NULL, verify); else @@ -136,7 +139,7 @@ int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, i -int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt) { +int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt, int armor) { FILE *in = NULL; FILE *out = NULL; pcp_pubkey_t *pubhash = NULL; /* FIXME: add free() */ @@ -266,14 +269,26 @@ int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *rec Pcpstream *pin = ps_new_file(in); Pcpstream *pout = ps_new_file(out); + if(armor == 1) { + ps_print(pout, PCP_ENFILE_HEADER); + ps_armor(pout, PCP_BLOCK_SIZE/2); + } + if(self == 1) clen = pcp_encrypt_stream_sym(pin, pout, symkey, 0, NULL); else clen = pcp_encrypt_stream(pin, pout, secret, pubhash, signcrypt); - ps_close(pin); + if(armor == 1) { + ps_finish(pout); + ps_unarmor(pout); + ps_print(pout, PCP_ENFILE_FOOTER); + } + ps_close(pout); + ps_close(pin); + if(clen > 0) { if(id == NULL && recipient == NULL) fprintf(stderr, "Encrypted %ld bytes symetrically\n", clen); diff --git a/src/encryption.h b/src/encryption.h index e757412..535fa7e 100644 --- a/src/encryption.h +++ b/src/encryption.h @@ -38,6 +38,6 @@ #include "pcpstream.h" int pcpdecrypt(char *id, int useid, char *infile, char *outfile, char *passwd, int verify); -int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt); +int pcpencrypt(char *id, char *infile, char *outfile, char *passwd, plist_t *recipient, int signcrypt, int armor); #endif /* _HAVE_ENCRYPTION_H */ diff --git a/src/pcp.c b/src/pcp.c index 7effe14..b2819e5 100644 --- a/src/pcp.c +++ b/src/pcp.c @@ -474,11 +474,11 @@ int main (int argc, char **argv) { if(useid == 1 && userec == 0) { /* one dst, FIXME: make id a list as well */ id = pcp_normalize_id(keyid); - pcpencrypt(id, infile, outfile, xpass, NULL, signcrypt); + pcpencrypt(id, infile, outfile, xpass, NULL, signcrypt, armor); } else if(useid == 0 && userec == 1) { /* multiple dst */ - pcpencrypt(NULL, infile, outfile, xpass, recipient, signcrypt); + pcpencrypt(NULL, infile, outfile, xpass, recipient, signcrypt, armor); } else { /* -i and -r specified */ @@ -558,7 +558,7 @@ int main (int argc, char **argv) { break; case PCP_MODE_ENCRYPT_ME: - pcpencrypt(NULL, infile, outfile, xpass, NULL, 0); + pcpencrypt(NULL, infile, outfile, xpass, NULL, 0, armor); break; case PCP_MODE_TEXT: diff --git a/tests/pipetest.c b/tests/pipetest.c index 605c28e..1621793 100644 --- a/tests/pipetest.c +++ b/tests/pipetest.c @@ -1,6 +1,7 @@ #include #include #include +#include #include diff --git a/tests/unittests.cfg b/tests/unittests.cfg index c35e94c..880ad91 100644 --- a/tests/unittests.cfg +++ b/tests/unittests.cfg @@ -35,6 +35,40 @@ include keys.cfg + + + md5 = `md5 -q ../COPYING` + cmd = ./pipetest 8 8 e < ../COPYING | ./pipetest 8 8 d | md5 -q + expect = /$md5/ + + + md5 = `md5 -q ../COPYING` + cmd = ./pipetest 8 16 e < ../COPYING | ./pipetest 8 16 d | md5 -q + expect = /$md5/ + + + md5 = `md5 -q ../COPYING` + cmd = ./pipetest 16 8 e < ../COPYING | ./pipetest 16 8 d | md5 -q + expect = /$md5/ + + + md5 = `md5 -q ../COPYING` + cmd = ./pipetest 64 32 e < ../COPYING | ./pipetest 64 32 d | md5 -q + expect = /$md5/ + + + md5 = `md5 -q ../COPYING` + cmd = ./pipetest 32 64 e < ../COPYING | ./pipetest 32 64 d | md5 -q + expect = /$md5/ + + + md5 = `md5 -q ../COPYING` + cmd = ./pipetest 64 64 e < ../COPYING | ./pipetest 64 64 d | md5 -q + expect = /$md5/ + + + + cmd = $pcp -h expect = /export/ diff --git a/tests/unittests.pl b/tests/unittests.pl index 09f4e16..cf39818 100755 --- a/tests/unittests.pl +++ b/tests/unittests.pl @@ -73,6 +73,10 @@ sub runtest { my($cfg, $name) = @_; my($in, $out, $error, $timeout); + foreach my $key (keys %{$cfg}) { + $cfg->{$key} =~ s/\`([^\`]*)\`/my $result = `$1`; chomp $result; $result/ge; + } + if (exists $cfg->{prepare}) { print STDERR " executing prepare command: $cfg->{prepare}\n" if ($verbose); if ($cfg->{prepare} =~ />/) {