diff --git a/TODO b/TODO index 9ee876a..6b440f3 100644 --- a/TODO +++ b/TODO @@ -17,6 +17,30 @@ vault checksum: add keysigs as well enable formats for secret key exports as well +Z85 headers: + - currently I use "----- BEGIN ... -----" and "----- END ... -----" as + header and footer for various z85 encoded outputs. The problem is, that + the "-" character is part of Z85 chars. An input of 0xc6,0x5a,0x0b,0x13 would + result z85 encoded as: "-----". So, I cannot be sure, when I find a header + delimiter, if it's really a delimiter or legitimate z85 encoded content. + Therefore, another delimiter must be used. "~~~~~ BEGIN .... ~~~~~" seems + to fit best and "~" is unused in Z85. + Then the parser can be enhanced as well. Eg: on startup if a ~ occurs, + ignore input until the first non-~ appears. Then decode input until a + ~ or eof appears, ignore everything after. Comments would still be a + problem though. Currently I ignore lines containing whitespaces. But + if a file is read blockwise and the blocksize is very small, then a + comment line may span multiple blocks and isn't recognizable as a + "line" anymore. Maybe, comments shall start and end with a ~ as well, eg: + ~ BEGIN KEY ~ + ~ Hash: 987298347 ~ + [z85] + ~ END KEY ~ + Here I use the same aproach for the headers, since there would also be + the problem how to recognize them properly if a header crosses boundaries + or something. By using this scheme, if a ~ is found everything following + is marked as to be ignored which could be saved as a state when using + blockmode. PCPSTREAM changes: - enable determine armor mode of input, parse headers, comments diff --git a/bindings/cpp/key++.h b/bindings/cpp/key++.h index 848edea..b4c7a9b 100644 --- a/bindings/cpp/key++.h +++ b/bindings/cpp/key++.h @@ -29,6 +29,7 @@ #include #include "helpers++.h" +#include "buffer++.h" namespace pcp { @@ -57,8 +58,6 @@ namespace pcp { void is_stored(bool s); bool is_stored(); - - std::string to_text(); }; bool operator!(PubKey& k); @@ -83,7 +82,7 @@ namespace pcp { const std::string& mail); Key(pcp_key_t *k); Key(pcp_key_t *k, bool store); - Key(std::string &z85encoded); + Key(std::string &z85encoded, std::string& passphrase); // destructor ~Key(); @@ -106,12 +105,14 @@ namespace pcp { bool is_encrypted(); bool is_primary(); - std::string to_text(); + std::string export_secret(const std::string& passphrase); + std::string export_public(); + }; // << and >> operators bool operator!(Key& k); - std::ostream& operator<<(std::ostream& output, Key& k); + //std::ostream& operator<<(std::ostream& output, Key& k); }; diff --git a/bindings/cpp/key.cpp b/bindings/cpp/key.cpp index 97681b0..9614216 100644 --- a/bindings/cpp/key.cpp +++ b/bindings/cpp/key.cpp @@ -62,39 +62,23 @@ Key::Key(pcp_key_t *k, bool store) { K = k; } -Key::Key(string &z85encoded) { +Key::Key(string &z85encoded, string &passphrase) { stored = false; if(z85encoded.length() == 0) throw pcp::exception("Error: zero length input"); - size_t clen; - unsigned char *z85decoded = pcp_z85_decode((char *)z85encoded.c_str(), &clen); + pcp_key_t *key = pcp_import_secret((unsigned char *)z85encoded.c_str(), z85encoded.length(), (char *)passphrase.c_str()); - if(z85decoded == NULL) - throw pcp::exception("Error: could not decode input - it's probably not Z85.\n"); - - if(clen != PCP_RAW_KEYSIZE) { - free(z85decoded); - char m[256]; - sprintf(m, "Error: decoded input didn't result to a proper sized key (got %ld bytes)!\n", clen); - throw pcp::exception(string(m)); - } - - // all good now, import the blob - pcp_key_t *key = (pcp_key_t *)ucmalloc(sizeof(pcp_key_t)); - memcpy(key, z85decoded, PCP_RAW_KEYSIZE); - key2native(key); + if(key == NULL) + throw pcp::exception(); if(pcp_sanitycheck_key(key) != 0) { free(key); - free(z85decoded); throw pcp::exception(); } K = key; - cout << 7 << " false" << endl; - } Key::~Key() { @@ -108,70 +92,42 @@ Key& Key::operator = (const Key &k) { return *this; } -string Key::to_text() { - size_t zlen; - pcp_key_t *key = K; +string Key::export_secret(const string &passphrase) { + Buffer *exported_sk; - key2be(key); - void *blob = ucmalloc(PCP_RAW_KEYSIZE); - pcp_seckeyblob(blob, key); - char *z85encoded = pcp_z85_encode((unsigned char*)blob, PCP_RAW_KEYSIZE, &zlen); + if(passphrase.length() == 0) + throw pcp::exception("Error: empty passphrase"); - if(PCP_ERRSET == 1) + exported_sk = pcp_export_secret(K, (char *)passphrase.c_str()); + + if(exported_sk == NULL) throw pcp::exception(); - key2native(key); + size_t zlen; + char *z85 = pcp_z85_encode(buffer_get(exported_sk), buffer_size(exported_sk), &zlen); - free(blob); + string out = string(EXP_SK_HEADER) + "\r\n" + string(z85) + "\r\n" + string(EXP_SK_FOOTER) + "\r\n"; - struct tm *c; - time_t t = (time_t)key->ctime; - c = localtime(&t); - - string z85; - char *out = (char *)ucmalloc(2048); - - sprintf(out, "%s\n", PCP_KEY_HEADER); - z85 += out; - - sprintf(out, " Generated by: %s Version %d.%d.%d\n", - PCP_ME, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); - z85 += out; - - sprintf(out, " Cipher: %s\n", PCP_KEY_PRIMITIVE); - z85 += out; - - sprintf(out, " Key-ID: 0x%s\n", key->id); - z85 += out; - - //2004-06-14T23:34:30. - sprintf(out, " Creation Time: %04d-%02d-%02dT%02d:%02d:%02d\n", - c->tm_year+1900, c->tm_mon+1, c->tm_mday, - c->tm_hour, c->tm_min, c->tm_sec); - z85 += out; - - sprintf(out, " Serial Number: 0x%08X\n", key->serial); - z85 += out; - - sprintf(out, " Key Version: 0x%08X\n", key->version); - z85 += out; - - sprintf(out, "\n%s\n", z85encoded); - z85 += out; - - sprintf(out, "%s\n", PCP_KEY_FOOTER); - z85 += out; - - free(z85encoded); - - return z85; + return out; } -ostream& pcp::operator<<(ostream& output, Key& k) { - output << k.to_text(); - return output; +string Key::export_public() { + Buffer *exported_pk; + + exported_pk = pcp_export_rfc_pub(K); + + if(exported_pk == NULL) + throw pcp::exception(); + + size_t zlen; + char *z85 = pcp_z85_encode(buffer_get(exported_pk), buffer_size(exported_pk), &zlen); + + string out = string(EXP_PK_HEADER) + "\r\n" + string(z85) + "\r\n" + string(EXP_PK_FOOTER) + "\r\n"; + + return out; } + bool pcp::operator!(Key& k) { if(k.K == NULL) return true; @@ -179,6 +135,7 @@ bool pcp::operator!(Key& k) { return false; } + void Key::encrypt(const string& passphrase) { K = pcpkey_encrypt(K, (char *)passphrase.c_str()); if(PCP_ERRSET == 1) @@ -254,21 +211,18 @@ PubKey::PubKey(pcp_pubkey_t *k, bool store) { K = k; } - -// FIXME: use Buffer class for stuff like this PubKey::PubKey(string &z85encoded) { stored = false; if(z85encoded.length() == 0) throw pcp::exception("Error: zero length input"); - Buffer *blob = buffer_new(256, "pub"); - buffer_add(blob, z85encoded.c_str(), z85encoded.length()); + Buf blob("pub", 256); + blob.add(z85encoded.c_str(), z85encoded.length()); - pcp_ks_bundle_t *KS = pcp_import_pub(buffer_get(blob), buffer_size(blob)); + pcp_ks_bundle_t *KS = pcp_import_pub(buffer_get(blob.get_buffer()), buffer_size(blob.get_buffer())); if(KS == NULL) { - buffer_free(blob); throw pcp::exception(); } pcp_pubkey_t *pub = KS->p; @@ -277,7 +231,6 @@ PubKey::PubKey(string &z85encoded) { free(KS->p); free(KS->s); free(KS); - buffer_free(blob); throw pcp::exception(); } @@ -295,104 +248,6 @@ PubKey& PubKey::operator = (const PubKey &k) { return *this; } -string PubKey::to_text() { - size_t zlen; - pcp_pubkey_t *key = K; - - pubkey2be(key); - void *blob = ucmalloc(PCP_RAW_PUBKEYSIZE); - pcp_pubkeyblob(blob, key); - char *z85encoded = pcp_z85_encode((unsigned char*)blob, PCP_RAW_PUBKEYSIZE, &zlen); - - if(PCP_ERRSET == 1) - throw pcp::exception(); - - pubkey2native(key); - - free(blob); - - struct tm *c; - time_t t = (time_t)key->ctime; - c = localtime(&t); - - char *out = (char *)ucmalloc(2048); - string z85; - - sprintf(out, "%s\n", PCP_PUBKEY_HEADER); - z85 += out; - - sprintf(out, " Generated by: %s Version %d.%d.%d\n", - PCP_ME, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); - z85 += out; - - sprintf(out, " Cipher: %s\n", PCP_KEY_PRIMITIVE); - z85 += out; - - sprintf(out, " PubKey-ID: 0x%s\n", key->id); - z85 += out; - - //2004-06-14T23:34:30. - sprintf(out, " Creation Time: %04d-%02d-%02dT%02d:%02d:%02d\n", - c->tm_year+1900, c->tm_mon+1, c->tm_mday, - c->tm_hour, c->tm_min, c->tm_sec); - z85 += out; - - unsigned char *hash = pcppubkey_getchecksum(key); - z85 += " Checksum: "; - - int i; - for ( i = 0;i <15 ;++i) { - sprintf(out, "%02X:",(unsigned int) hash[i]); - z85 += out; - } - sprintf(out, "%02X", hash[15]); - z85 += out; - z85 += "\n "; - - for ( i = 16;i <31 ;++i) { - sprintf(out, "%02X:",(unsigned int) hash[i]); - z85 += out; - } - sprintf(out, "%02X", hash[31]); - z85 += out; - z85 += "\n"; - - sprintf(out, " Serial Number: 0x%08X\n", key->serial); - z85 += out; - sprintf(out, " Key Version: 0x%08X\n", key->version); - z85 += out; - - char *r = pcppubkey_get_art(key); - z85 += " Random Art ID: "; - int rlen = strlen(r); - - for (i=0; i #include +#include #include "mem.h" #include "util.h" #include "defines.h" diff --git a/include/pcp/z85.h b/include/pcp/z85.h index 0b49dcb..a82e3b6 100644 --- a/include/pcp/z85.h +++ b/include/pcp/z85.h @@ -131,6 +131,17 @@ char *pcp_readz85string(unsigned char *input, size_t bufsize); */ size_t _buffer_is_binary(unsigned char *buf, size_t len); + +/** Determine if a char is a Z85 character + + \param[out] z Buffer object where to put the char if it's z85 and not inside a comment. + \param[in] c The char to check. + \param[in] is_comment Denotes if we're currently within a comment. + + \return Returns 1 if a comment starts or 0 otherwise. + */ +uint8_t _parse_zchar(Buffer *z, uint8_t c, uint8_t is_comment); + #endif /* _HAVE_PCP_Z85_H */ /**@}*/ diff --git a/libpcp/pcpstream.c b/libpcp/pcpstream.c index 7fcecbd..20c2939 100644 --- a/libpcp/pcpstream.c +++ b/libpcp/pcpstream.c @@ -56,8 +56,9 @@ Pcpstream *ps_new_outbuffer() { } void ps_setdetermine(Pcpstream *stream, size_t blocksize) { + assert(blocksize % 4 == 0); stream->determine = 1; - stream->blocksize = blocksize; + stream->blocksize = blocksize + (5 - (blocksize % 5)); if(stream->cache == NULL) { stream->cache = buffer_new(32, "Pcpstreamcache"); stream->next = buffer_new(32, "Pcpstreamcachenext"); @@ -65,6 +66,7 @@ void ps_setdetermine(Pcpstream *stream, size_t blocksize) { } void ps_armor(Pcpstream *stream, size_t blocksize) { + assert(blocksize % 4 == 0); stream->armor = 1; stream->blocksize = blocksize; if(stream->cache == NULL) { @@ -196,6 +198,53 @@ void ps_determine(Pcpstream *stream) { } size_t ps_read_decode(Pcpstream *stream, Buffer *cache, void *buf, size_t bufsize) { + size_t zdiff = 1; + size_t i = 0; + uint8_t is_comment = 0; + uint8_t c; + Buffer *z = buffer_new(32, "ztemp"); + byte *_buf = buf; + + if(bufsize > 0) { + for(i=0; iblocksize) { + /* blocksize not full, continue with stream source */ + /* read in bytewise, ignore newlines and add until the block is full */ + while (buffer_size(z) < stream->blocksize) { + if (ps_read_raw(stream, &c, 1) == 1) { + is_comment = _parse_zchar(z, c, is_comment); + } + else + break; + } + } + + /* finally, decode it and put into cache */ + size_t binlen, outlen; + unsigned char *bin = pcp_z85_decode(buffer_get_str(z), &binlen); + if(bin == NULL) { + /* it's not z85 encoded, so threat it as binary */ + stream->armor = 1; + buffer_add_buf(stream->cache, z); + outlen = buffer_size(stream->cache); + } + else { + /* yes, successfully decoded it, put into cache */ + buffer_add(stream->cache, bin, binlen); + outlen = binlen; + } + + buffer_free(z); + + return outlen; +} + +size_t ps_read_decodeOLD(Pcpstream *stream, Buffer *cache, void *buf, size_t bufsize) { size_t zdiff = 1; size_t i = 0; Buffer *z = buffer_new(32, "ztemp"); diff --git a/libpcp/z85.c b/libpcp/z85.c index 8095eaa..5ab75dd 100644 --- a/libpcp/z85.c +++ b/libpcp/z85.c @@ -37,6 +37,20 @@ size_t _buffer_is_binary(unsigned char *buf, size_t len) { return 0; } +uint8_t _parse_zchar(Buffer *z, uint8_t c, uint8_t is_comment) { + if(is_comment == 1) { + if(c == '~') + is_comment = 0; + } + else { + if(c == '~') + is_comment = 1; + else if(c != '\r' && c != '\n') { + buffer_add8(z, c); + } + } + return is_comment; +} unsigned char *pcp_padfour(unsigned char *src, size_t srclen, size_t *dstlen) { size_t outlen, zerolen; @@ -172,54 +186,12 @@ char *pcp_readz85string(unsigned char *input, size_t bufsize) { } Buffer *z = buffer_new(MAXLINE, "z"); - Buffer *line = buffer_new(MAXLINE, "line"); - char *oneline; - int begin, end; - begin = end = 0; + uint8_t is_comment = 0; char *out = NULL; - for(i=0; i 0 && end != 1) { - /* something left in line buffer, probably - newline at eof missing or no multiline input */ - buffer_add_buf(z, line); - } + for(i=0; ictime; c = localtime(&t); - fprintf(out, " Generated by: %s Version %d.%d.%d\n", - PCP_ME, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); + fprintf(out, " Cipher: %s\n", PCP_KEY_PRIMITIVE); - fprintf(out, " Cipher: %s\n", PCP_KEY_PRIMITIVE); + fprintf(out, " Owner: %s\n", key->owner); + fprintf(out, " Mail: %s\n", key->mail); - fprintf(out, " Owner: %s\n", key->owner); - fprintf(out, " Mail: %s\n", key->mail); - - fprintf(out, " Key-ID: 0x%s\n", key->id); - fprintf(out, " Public-Key: %s\n", pcp_z85_encode(key->pub, 32, &zlen)); + fprintf(out, " Key-ID: 0x%s\n", key->id); + fprintf(out, " Public-Key: %s\n", pcp_z85_encode(key->pub, 32, &zlen)); /* 2004-06-14T23:34:30. */ fprintf(out, " Creation Time: %04d-%02d-%02dT%02d:%02d:%02d\n", @@ -199,23 +160,23 @@ void pcppubkey_print(pcp_pubkey_t *key, FILE* out) { c->tm_hour, c->tm_min, c->tm_sec); unsigned char *hash = pcppubkey_getchecksum(key); - fprintf(out, " Checksum: "); + fprintf(out, " Checksum: "); int i; for ( i = 0;i <15 ;++i) fprintf(out, "%02X:",(unsigned int) hash[i]); fprintf(out, "%02X", hash[15]); - fprintf(out, "\n "); + fprintf(out, "\n "); for ( i = 16;i <31 ;++i) fprintf(out, "%02X:",(unsigned int) hash[i]); fprintf(out, "%02X", hash[31]); fprintf(out, "\n"); fprintf(out, " Serial Number: 0x%08X\n", key->serial); - fprintf(out, " Key Version: 0x%08X\n", key->version); + fprintf(out, " Key Version: 0x%08X\n", key->version); char *r = pcppubkey_get_art(key); fprintf(out, " Random Art ID: "); for (i=0; ictime; c = localtime(&t); - fprintf(out, "%s\n", PCP_KEY_HEADER); + fprintf(out, " Cipher: %s\n", PCP_KEY_PRIMITIVE); - fprintf(out, " Generated by: %s Version %d.%d.%d\n", - PCP_ME, PCP_VERSION_MAJOR, PCP_VERSION_MINOR, PCP_VERSION_PATCH); - - fprintf(out, " Cipher: %s\n", PCP_KEY_PRIMITIVE); - - fprintf(out, " Key-ID: 0x%s\n", key->id); + fprintf(out, " Key-ID: 0x%s\n", key->id); /* 2004-06-14T23:34:30. */ fprintf(out, " Creation Time: %04d-%02d-%02dT%02d:%02d:%02d\n", - c->tm_year+1900, c->tm_mon+1, c->tm_mday, - c->tm_hour, c->tm_min, c->tm_sec); + c->tm_year+1900, c->tm_mon+1, c->tm_mday, + c->tm_hour, c->tm_min, c->tm_sec); fprintf(out, " Serial Number: 0x%08X\n", key->serial); - fprintf(out, " Key Version: 0x%08X\n", key->version); + fprintf(out, " Key Version: 0x%08X\n", key->version); - fprintf(out, "\n%s\n", z85encoded); - - fprintf(out, "%s\n", PCP_KEY_FOOTER); - - free(z85encoded); } - void pcpkey_printshortinfo(pcp_key_t *key) { int i; printf(" Key-ID: 0x%s\n", key->id); diff --git a/tests/streamtest.c b/tests/streamtest.c index 36fcb62..101dcb7 100644 --- a/tests/streamtest.c +++ b/tests/streamtest.c @@ -20,6 +20,7 @@ int main() { /* out output stream, z85 encoded, use z85 blocksize 8 */ Pcpstream *pout = ps_new_file(out); + ps_print(pout, "~~~~~ BEGIN ~~~~~\r\n"); ps_armor(pout, 8); /* "encrypt" a couple of times into the output stream */ @@ -31,6 +32,8 @@ int main() { /* done, put cached buffers out and close */ ps_finish(pout); + pout->armor = 0; + ps_print(pout, "\r\n~~~~~ END ~~~~~\r\n"); ps_close(pout); fclose(out); @@ -41,10 +44,8 @@ int main() { } Pcpstream *pin = ps_new_file(in); - /* enable autmatically encoding detection. - set blocksize to 10, because: - 8 + (5 - (8 % 5)) == 10 */ - ps_setdetermine(pin, 10); + /* enable autmatically encoding detection. */ + ps_setdetermine(pin, 8); /* we'll use this stream to put the "decrypted" data in. note, that this could be a file as well. */