diff --git a/src/algo_suite.h b/src/algo_suite.h index 2ec2eab..a068a86 100644 --- a/src/algo_suite.h +++ b/src/algo_suite.h @@ -21,9 +21,10 @@ #include #include + class algorithm; + typedef std::map algorithm_suite; void fill_algorithm_suite (algorithm_suite&); - #endif diff --git a/src/chacha.cpp b/src/chacha.cpp index dbdec9a..fb09d93 100644 --- a/src/chacha.cpp +++ b/src/chacha.cpp @@ -75,7 +75,7 @@ void chacha20::init() for (int i = 0; i < 10; ++i) key[i] = 0; for (int i = 0; i < 2; ++i) counter[i] = 0; - blockpos = 256; + blockpos = 64; } void chacha20::load_key (const byte*begin, const byte*end) @@ -116,7 +116,7 @@ void chacha20::gen (size_t n, byte*out) //fill in whole blocks while (n >= 64) { - if (out) chacha_gen (key, counter, (uint32_t*) &out); + if (out) chacha_gen (key, counter, (uint32_t*) out); chacha_incr_counter (counter); out += 64; diff --git a/src/cube_hash.h b/src/cube_hash.h index 3f8ff3c..7de86f8 100644 --- a/src/cube_hash.h +++ b/src/cube_hash.h @@ -61,21 +61,22 @@ public: bpos = 0; } - void eat (const std::vector&a) { + void eat (const byte*a, const byte*aend) { int apos = 0; + int asize = aend - a; if (bpos) { - for (; bpos < B && apos < a.size(); ++bpos, ++apos) + for (; bpos < B && apos < asize; ++bpos, ++apos) buf[bpos] = a[apos]; if (bpos == B) { state.process_block (buf); bpos = 0; } } - while (apos + B <= a.size() ) { + while (apos + B <= asize ) { state.process_block (& (a[apos]) ); apos += B; } - for (; apos < a.size(); ++apos, ++bpos) + for (; apos < asize; ++apos, ++bpos) buf[bpos] = a[apos]; } diff --git a/src/hash.cpp b/src/hash.cpp index e62d5c6..9575025 100644 --- a/src/hash.cpp +++ b/src/hash.cpp @@ -18,6 +18,8 @@ #include "hash.h" +#include "str_match.h" + #include "sha_hash.h" #include "rmd_hash.h" #include "tiger_hash.h" @@ -29,7 +31,7 @@ hash_proc::suite_t& hash_proc::suite() #define do_hash(name,type) \ static factoryof type##_var; \ - s[name]=&type##_var; + s[to_unicase(name)]=&type##_var; if (s.empty() ) { do_hash ("CUBE512", cube512proc); diff --git a/src/hash.h b/src/hash.h index 2a49cfa..1f8f6c1 100644 --- a/src/hash.h +++ b/src/hash.h @@ -40,10 +40,15 @@ class hash_proc public: virtual uint size() = 0; virtual void init() = 0; - virtual void eat (const std::vector&) = 0; + + virtual void eat (const byte*begin, const byte*end) = 0; virtual std::vector finish() = 0; virtual ~hash_proc() {} + void eat (const std::vector&a) { + return eat (& (a[0]), & (a[a.size()]) ); + } + typedef std::map*> suite_t; static suite_t& suite(); }; diff --git a/src/hashfile.cpp b/src/hashfile.cpp index db746a1..484612f 100644 --- a/src/hashfile.cpp +++ b/src/hashfile.cpp @@ -42,8 +42,8 @@ class size64proc : public hash_proc s = 0; } - void eat (const std::vector&a) { - s += a.size(); + void eat (const byte*a, const byte*aend) { + s += aend - a; } std::vector finish() { diff --git a/src/sc.cpp b/src/sc.cpp index 69dacaf..25ab89c 100644 --- a/src/sc.cpp +++ b/src/sc.cpp @@ -18,6 +18,8 @@ #include "sc.h" +#include "str_match.h" + #include "arcfour.h" #include "xsynd.h" #include "chacha.h" @@ -29,7 +31,7 @@ streamcipher::suite_t& streamcipher::suite() static suite_t s; #define do_cipher(name,type) \ static factoryof type##_var; \ - s[name]=&type##_var; + s[to_unicase(name)]=&type##_var; if (s.empty() ) { do_cipher ("ARCFOUR", arcfour_t); diff --git a/src/serialization.cpp b/src/serialization.cpp index 735e698..135f3c9 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -681,7 +681,7 @@ bool hashfile::unserialize (sencode*s) * ( streamcipher1 streamcipher2 ) * ( hash1 hash2 hash3 ) * int_blocksize - * seed_data + * key_data * ) */ @@ -695,13 +695,13 @@ sencode* symkey::serialize() L->items.resize (5); L->items[0] = new sencode_bytes (SYMKEY_IDENT); L->items[3] = new sencode_int (blocksize); - L->items[4] = new sencode_bytes (seed); + L->items[4] = new sencode_bytes (key); + std::set::iterator i, e; LL = new sencode_list(); LL->items.resize (ciphers.size() ); k = 0; - for (std::set::iterator - i = ciphers.begin(), e = ciphers.end(); + for (i = ciphers.begin(), e = ciphers.end(); i != e; ++i) LL->items[k++] = new sencode_bytes (*i); L->items[1] = LL; @@ -709,8 +709,7 @@ sencode* symkey::serialize() LL = new sencode_list(); LL->items.resize (hashes.size() ); k = 0; - for (std::list::iterator - i = hashes.begin(), e = hashes.end(); + for (i = hashes.begin(), e = hashes.end(); i != e; ++i) LL->items[k++] = new sencode_bytes (*i); L->items[2] = LL; @@ -734,8 +733,8 @@ bool symkey::unserialize (sencode*s) sencode_bytes*B; CAST_BYTES (L->items[4], B); - seed.clear(); - seed.insert (seed.begin(), B->b.begin(), B->b.end() ); + key.clear(); + key.insert (key.begin(), B->b.begin(), B->b.end() ); sencode_list*LL; uint i; @@ -752,7 +751,8 @@ bool symkey::unserialize (sencode*s) hashes.clear(); for (i = 0; i < LL->items.size(); ++i) { CAST_BYTES (LL->items[i], B); - hashes.push_back (B->b); + if (hashes.count (B->b) ) return false; + hashes.insert (B->b); } return true; diff --git a/src/sha_hash.h b/src/sha_hash.h index 511841e..b11b44f 100644 --- a/src/sha_hash.h +++ b/src/sha_hash.h @@ -60,8 +60,8 @@ public: state.Restart(); } - void eat (const std::vector&a) { - state.Update (& (a[0]), a.size() ); + void eat (const byte*a, const byte*aend) { + state.Update (a, aend - a); } std::vector finish() { diff --git a/src/str_match.cpp b/src/str_match.cpp index ccb55c9..d4ca38e 100644 --- a/src/str_match.cpp +++ b/src/str_match.cpp @@ -54,3 +54,9 @@ bool keyspec_matches (const std::string&search, return matches_icase (name, search); } + +std::string to_unicase (std::string str) +{ + transform (str.begin(), str.end(), str.begin(), ::toupper); + return str; +} diff --git a/src/str_match.h b/src/str_match.h index d603f0b..0118219 100644 --- a/src/str_match.h +++ b/src/str_match.h @@ -28,4 +28,6 @@ bool keyspec_matches (const std::string&search, const std::string&name, const std::string&keyid); +std::string to_unicase (std::string); + #endif diff --git a/src/symkey.cpp b/src/symkey.cpp index 336dbcf..9ada4bb 100644 --- a/src/symkey.cpp +++ b/src/symkey.cpp @@ -18,14 +18,320 @@ #include "symkey.h" -bool symkey::encrypt (std::istream&in, std::ostream&out) -{ +#include "sc.h" +#include "hash.h" +#include "str_match.h" +#include "iohelpers.h" - return false; +#include + +bool symkey::is_valid() +{ + return blocksize >= 1024 && + blocksize < 0x10000000 && //256M + !ciphers.empty() && + !hashes.empty() && + key.size() >= 32 && //not less than 256bits of key stuff + key.size() < 2048; +} + +bool symkey::create (const std::string&in, prng&rng) +{ + //first, find cipher and hash names + blocksize = 1024 * 1024; + uint keysize = 32; + std::stringstream ss (in); + std::string tok; + while (getline (ss, tok, ',') ) { + tok = to_unicase (tok); + if (tok == "SHORTBLOCK") blocksize = 1024; + else if (tok == "LONGBLOCK") blocksize = 64 * 1024 * 1024; + else if (tok == "LONGKEY") keysize = 256; + else if (streamcipher::suite().count (tok) ) + ciphers.insert (tok); + else if (hash_proc::suite().count (tok) ) + hashes.insert (tok); + else { + err ("symkey: unknown token: " << tok); + return false; + } + } + + //increase keysize, if needed + for (std::set::iterator + i = ciphers.begin(), e = ciphers.end(); + i != e; ++i) { + + instanceof sc + (streamcipher::suite() [*i]->get() ); + sc.collect(); + if (sc->key_size() > keysize) keysize = sc->key_size(); + } + + + //fill the key + key.resize (keysize); + for (uint i = 0; i < keysize; ++i) key[i] = rng.random (256); + + if (!is_valid() ) { + err ("symkey: failed to produce valid symmetric key"); + err ("symkey: check that at least one hash and cipher is used"); + return false; + } + + return true; +} + +typedef std::list > scs_t; +typedef std::list > hashes_t; + +bool symkey::encrypt (std::istream&in, std::ostream&out, prng&rng) +{ + if (!is_valid() ) return false; + + /* + * structure of symmetrically encrypted file: + * + * - one-time key part, key.size() bytes + * (repeat: + * - 4B blocksize little-endian + * - blocksize encrypted bytes + * - sum(hashes's size) blocksize marker+bytes of block hashes + * ) + * - 4B less than blocksize (may be zero!) + * - possibly incomplete last block (may be empty) + * - hashes of last blocksize+block + * - eof + */ + + std::vector otkey; + otkey.resize (key.size() ); + for (uint i = 0; i < otkey.size(); ++i) otkey[i] = rng.random (256); + + /* + * initialize the ciphers + */ + + scs_t scs; + for (std::set::iterator + i = ciphers.begin(), e = ciphers.end(); + i != e; ++i) { + if (!streamcipher::suite().count (*i) ) { + err ("symkey: unsupported cipher: " << *i); + return false; + } + scs.push_back (streamcipher::suite() [*i]->get() ); + scs.back().collect(); + scs.back()->init(); + scs.back()->load_key_vector (key); + scs.back()->load_key_vector (otkey); + } + + /* + * initialize the hashes + */ + + uint hashes_size = 0; + + hashes_t hs; + for (std::set::iterator + i = hashes.begin(), e = hashes.end(); + i != e; ++i) { + if (!hash_proc::suite().count (*i) ) { + err ("symkey: unsupported hash function: " << *i); + return false; + } + hs.push_back (hash_proc::suite() [*i]->get() ); + hs.back().collect(); + + hashes_size += hs.back()->size(); + } + + /* + * output the onetime key + */ + + out.write ( (char*) & (otkey[0]), otkey.size() ); + + /* + * process the blocks + */ + + std::vectorbuf, cipbuf; + buf.resize (4 + blocksize + hashes_size); + cipbuf.resize (buf.size() ); + + for (;;) { + in.read ( (char*) & (buf[4]), blocksize); + uint bytes_read = in.gcount(); + + if (!in && !in.eof() ) { + err ("symkey: failed reading input"); + return false; + } + + //now we got bytes_read of key stuff ready in buf. + uint blksizeid = bytes_read; + for (uint i = 0; i < 4; ++i) { + buf[i] = blksizeid & 0xff; + blksizeid >>= 8; + } + + //hashup! + uint hashpos = 4 + bytes_read; + for (hashes_t::iterator i = hs.begin(), e = hs.end(); + i != e; ++i) { + hash_proc&hp = **i; + hp.init(); + hp.eat (& (buf[0]), & (buf[4 + bytes_read]) ); + std::vector res = hp.finish(); + for (uint j = 0; j < res.size(); ++j, ++hashpos) + buf[hashpos] = res[j]; + } + + //encrypt! + for (scs_t::iterator i = scs.begin(), e = scs.end(); + i != e; ++i) { + streamcipher&sc = **i; + sc.gen (hashpos, & (cipbuf[0]) ); + for (uint j = 0; j < hashpos; ++j) + buf[j] = buf[j] ^ cipbuf[j]; + } + + //output! + out.write ( (char*) & (buf[0]), hashpos); + if (!out) { + err ("symkey: failed to write output"); + return false; + } + + //this was the last one + if (bytes_read < blocksize) break; + } + + return true; } int symkey::decrypt (std::istream&in, std::ostream&out) { + if (!is_valid() ) return 1; - return 1; + std::vector otkey; + otkey.resize (key.size() ); + + /* + * read otkey + */ + + in.read ( (char*) & (otkey[0]), otkey.size() ); + if (in.gcount() != (std::streamsize) otkey.size() || !in) { + err ("symkey: failed reading input"); + return 1; + } + + /* + * initialize the ciphers + */ + + scs_t scs; + for (std::set::iterator + i = ciphers.begin(), e = ciphers.end(); + i != e; ++i) { + if (!streamcipher::suite().count (*i) ) { + err ("symkey: unsupported cipher: " << *i); + return 1; + } + scs.push_back (streamcipher::suite() [*i]->get() ); + scs.back().collect(); + scs.back()->init(); + scs.back()->load_key_vector (key); + scs.back()->load_key_vector (otkey); + } + + /* + * initialize the hashes + */ + + uint hashes_size = 0; + + hashes_t hs; + for (std::set::iterator + i = hashes.begin(), e = hashes.end(); + i != e; ++i) { + if (!hash_proc::suite().count (*i) ) { + err ("symkey: unsupported hash function: " << *i); + return 1; + } + hs.push_back (hash_proc::suite() [*i]->get() ); + hs.back().collect(); + + hashes_size += hs.back()->size(); + } + + /* + * process the blocks + */ + + std::vector buf, cipbuf; + buf.resize (4 + blocksize + hashes_size); + cipbuf.resize (buf.size() ); + + for (;;) { + in.read ( (char*) & (buf[0]), buf.size() ); + uint bytes_read = in.gcount(); + + if ( (!in && !in.eof() ) || bytes_read < 4 + hashes_size) { + err ("symkey: failed reading input"); + return 1; + } + + //decrypt! + for (scs_t::iterator i = scs.begin(), e = scs.end(); + i != e; ++i) { + streamcipher&sc = **i; + sc.gen (bytes_read, & (cipbuf[0]) ); + for (uint j = 0; j < bytes_read; ++j) + buf[j] = buf[j] ^ cipbuf[j]; + } + + //verify the size + bytes_read -= (4 + hashes_size); + + uint blksizeid = bytes_read; + for (uint i = 0; i < 4; ++i) { + if (buf[i] != (blksizeid & 0xff) ) { + err ("symkey: mangled input"); + return 3; + } + blksizeid >>= 8; + } + + //verify the hashes + uint hashpos = 4 + bytes_read; + for (hashes_t::iterator i = hs.begin(), e = hs.end(); + i != e; ++i) { + hash_proc&hp = **i; + hp.init(); + hp.eat (& (buf[0]), & (buf[4 + bytes_read]) ); + std::vector res = hp.finish(); + for (uint j = 0; j < res.size(); ++j, ++hashpos) + if (buf[hashpos] != res[j]) { + err ("symkey: mangled input"); + return 3; + } + } + + //now that all is OK, output! + out.write ( (char*) & (buf[4]), bytes_read); + + //last one + if (bytes_read < blocksize) break; + } + + //did we read whole input? + if (!in.eof() ) { + err ("symkey: failed reading input"); + return 1; + } + return 0; } diff --git a/src/symkey.h b/src/symkey.h index f0e960c..4cfe7cc 100644 --- a/src/symkey.h +++ b/src/symkey.h @@ -26,23 +26,26 @@ #include #include "types.h" +#include "generator.h" #include "sencode.h" class symkey { public: - std::set ciphers; - std::list hashes; + std::set ciphers, hashes; uint blocksize; - std::vector seed; + std::vector key; sencode* serialize(); bool unserialize (sencode*); - bool encrypt (std::istream&, std::ostream&); + bool encrypt (std::istream&, std::ostream&, prng&); int decrypt (std::istream&, std::ostream&); + + bool is_valid(); + bool create (const std::string&, prng&); }; #endif