diff options
author | lloyd <[email protected]> | 2010-02-01 16:29:38 +0000 |
---|---|---|
committer | lloyd <[email protected]> | 2010-02-01 16:29:38 +0000 |
commit | 454e45b7c4fece11a7f43ffa412148b4a274c90f (patch) | |
tree | 5ae87c2104fba534548e59fa477d6a5f2f5a5e29 /doc | |
parent | ae6a404ec14cc3c86a96cd3e5c67c9c23be38147 (diff) |
Modify the S2K interface. Instead of being stateful in terms of the salt
and iteration count, force it to be passed to each call to derive_key.
So remove current_salt, set_iterations, new_random_salt, and change_salt
functions from S2K interface.
Update examples and test application to match.
While I was in there, change the passhash example to use 64 bit salts
and 128 bit PBKDF2 outputs.
Diffstat (limited to 'doc')
-rw-r--r-- | doc/api.tex | 81 | ||||
-rw-r--r-- | doc/examples/decrypt.cpp | 20 | ||||
-rw-r--r-- | doc/examples/encrypt.cpp | 21 | ||||
-rw-r--r-- | doc/examples/encrypt2.cpp | 15 | ||||
-rw-r--r-- | doc/examples/passhash.cpp | 27 | ||||
-rw-r--r-- | doc/examples/row_encryptor.cpp | 7 |
6 files changed, 99 insertions, 72 deletions
diff --git a/doc/api.tex b/doc/api.tex index 556e76aa0..20177a9f8 100644 --- a/doc/api.tex +++ b/doc/api.tex @@ -2621,53 +2621,46 @@ degree of applicability. \subsection{S2K Algorithms} -There are various procedures (usually fairly ad-hoc) for turning a passphrase -into a (mostly) arbitrary length key for a symmetric cipher. A general -interface for such algorithms is presented in \filename{s2k.h}. The main -function is \function{derive\_key}, which takes a passphrase, and the desired -length of the output key, and returns a key of that length, deterministically -produced from the passphrase. If an algorithm can't produce a key of that size, -it will throw an exception (most notably, PKCS \#5's PBKDF1 can only produce -strings between 1 and $n$ bytes, where $n$ is the output size of the underlying -hash function). - -Most such algorithms allow the use of a ``salt'', which provides some extra -randomness and helps against dictionary attacks on the passphrase. Simply call -\function{change\_salt} (there are variations of it for most of the ways you -might wish to specify a salt, check the header for details) with a block of -random data. You can also have the class generate a new salt for you with -\function{new\_random\_salt}; the salt that was generated can be retrieved with -\function{current\_salt}. - -Additionally some algorithms allow you to set some sort of iteration -count, which will make the algorithm take longer to compute the final -key (reducing the speed of brute-force attacks of various kinds). This -can be changed with the \function{set\_iterations} function. Most -standards recommend an iteration count of at least 1000. Currently -defined S2K algorithms are ``PBKDF1(digest)'', ``PBKDF2(digest)'', and -``OpenPGP-S2K(digest)''; you can retrieve any of these using the -\function{get\_s2k}, found in \filename{lookup.h}. As of this writing, -``PBKDF2(SHA-256)'' with 10000 iterations and an 8 byte salt is -recommend for new applications. +There are various procedures (usually fairly ad-hoc) for turning a +passphrase into a (mostly) arbitrary length key for a symmetric +cipher. A general interface for such algorithms is presented in +\filename{s2k.h}. The main function is \function{derive\_key}, which +takes a passphrase, a salt, an iteration count, and the desired length +of the output key, and returns a key of that length, deterministically +produced from the passphrase and salt. If an algorithm can't produce a +key of that size, it will throw an exception (most notably, PKCS \#5's +PBKDF1 can only produce strings between 1 and $n$ bytes, where $n$ is +the output size of the underlying hash function). + +The purpose of the iteration count is to make the algorithm take +longer to compute the final key (reducing the speed of brute-force +attacks of various kinds). Most standards recommend an iteration count +of at least 10000. Currently defined S2K algorithms are +``PBKDF1(digest)'', ``PBKDF2(digest)'', and ``OpenPGP-S2K(digest)''; +you can retrieve any of these using the \function{get\_s2k}, found in +\filename{lookup.h}. As of this writing, ``PBKDF2(SHA-256)'' with +10000 iterations and a 16 byte salt is recommend for new applications. \subsubsection{OpenPGP S2K} -There are some oddities about OpenPGP's S2K algorithms that are documented -here. For one thing, it uses the iteration count in a strange manner; instead -of specifying how many times to iterate the hash, it tells how many -\emph{bytes} should be hashed in total (including the salt). So the exact -iteration count will depend on the size of the salt (which is fixed at 8 bytes -by the OpenPGP standard, though the implementation will allow any salt size) -and the size of the passphrase. - -To get what OpenPGP calls ``Simple S2K'', set iterations to 0 (the default for -OpenPGP S2K), and do not specify a salt. To get ``Salted S2K'', again leave the -iteration count at 0, but give an 8-byte salt. ``Salted and Iterated S2K'' -requires an 8-byte salt and some iteration count (this should be significantly -larger than the size of the longest passphrase that might reasonably be used; -somewhere from 1024 to 65536 would probably be about right). Using both a -reasonably sized salt and a large iteration count is highly recommended to -prevent password guessing attempts. +There are some oddities about OpenPGP's S2K algorithms that are +documented here. For one thing, it uses the iteration count in a +strange manner; instead of specifying how many times to iterate the +hash, it tells how many \emph{bytes} should be hashed in total +(including the salt). So the exact iteration count will depend on the +size of the salt (which is fixed at 8 bytes by the OpenPGP standard, +though the implementation will allow any salt size) and the size of +the passphrase. + +To get what OpenPGP calls ``Simple S2K'', set iterations to 0, and do +not specify a salt. To get ``Salted S2K'', again leave the iteration +count at 0, but give an 8-byte salt. ``Salted and Iterated S2K'' +requires an 8-byte salt and some iteration count (this should be +significantly larger than the size of the longest passphrase that +might reasonably be used; somewhere from 1024 to 65536 would probably +be about right). Using both a reasonably sized salt and a large +iteration count is highly recommended to prevent password guessing +attempts. \subsection{Checksums} diff --git a/doc/examples/decrypt.cpp b/doc/examples/decrypt.cpp index ebab5d804..de261b5f3 100644 --- a/doc/examples/decrypt.cpp +++ b/doc/examples/decrypt.cpp @@ -106,12 +106,22 @@ int main(int argc, char* argv[]) const u32bit iv_len = block_size_of(algo); std::auto_ptr<S2K> s2k(get_s2k("PBKDF2(SHA-1)")); - s2k->set_iterations(8192); - s2k->change_salt(b64_decode(salt_str)); - SymmetricKey bc_key = s2k->derive_key(key_len, "BLK" + passphrase); - InitializationVector iv = s2k->derive_key(iv_len, "IVL" + passphrase); - SymmetricKey mac_key = s2k->derive_key(16, "MAC" + passphrase); + const u32bit PBKDF2_ITERATIONS = 8192; + + SecureVector<byte> salt = b64_decode(salt_str); + + SymmetricKey bc_key = s2k->derive_key(key_len, "BLK" + passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS); + + InitializationVector iv = s2k->derive_key(iv_len, "IVL" + passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS); + + SymmetricKey mac_key = s2k->derive_key(16, "MAC" + passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS); Pipe pipe(new Base64_Decoder, get_cipher(algo + "/CBC", bc_key, iv, DECRYPTION), diff --git a/doc/examples/encrypt.cpp b/doc/examples/encrypt.cpp index f903c2f24..4999fa086 100644 --- a/doc/examples/encrypt.cpp +++ b/doc/examples/encrypt.cpp @@ -125,17 +125,26 @@ int main(int argc, char* argv[]) AutoSeeded_RNG rng; std::auto_ptr<S2K> s2k(get_s2k("PBKDF2(SHA-1)")); - s2k->set_iterations(8192); - s2k->new_random_salt(rng, 8); - SymmetricKey bc_key = s2k->derive_key(key_len, "BLK" + passphrase); - InitializationVector iv = s2k->derive_key(iv_len, "IVL" + passphrase); - SymmetricKey mac_key = s2k->derive_key(16, "MAC" + passphrase); + SecureVector<byte> salt(8); + rng.randomize(&salt[0], salt.size()); + + const u32bit PBKDF2_ITERATIONS = 8192; + + SymmetricKey bc_key = s2k->derive_key(key_len, "BLK" + passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS); + InitializationVector iv = s2k->derive_key(iv_len, "IVL" + passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS); + SymmetricKey mac_key = s2k->derive_key(16, "MAC" + passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS); // Just to be all fancy we even write a (simple) header. out << "-------- ENCRYPTED FILE --------" << std::endl; out << algo << std::endl; - out << b64_encode(s2k->current_salt()) << std::endl; + out << b64_encode(salt) << std::endl; Pipe pipe(new Fork( new Chain(new MAC_Filter("HMAC(SHA-1)", mac_key), diff --git a/doc/examples/encrypt2.cpp b/doc/examples/encrypt2.cpp index dac2f8314..41f4fb478 100644 --- a/doc/examples/encrypt2.cpp +++ b/doc/examples/encrypt2.cpp @@ -26,16 +26,21 @@ int main() PKCS5_PBKDF2 pbkdf2(new HMAC(new SHA_160)); - pbkdf2.set_iterations(4096); - pbkdf2.new_random_salt(rng, 8); - SecureVector<byte> the_salt = pbkdf2.current_salt(); + const u32bit PBKDF2_ITERATIONS = 8192; - SecureVector<byte> master_key = pbkdf2.derive_key(48, passphrase).bits_of(); + SecureVector<byte> salt(8); + rng.randomize(&salt[0], salt.size()); + + SecureVector<byte> master_key = pbkdf2.derive_key(48, passphrase, + &salt[0], salt.size(), + PBKDF2_ITERATIONS).bits_of(); KDF* kdf = get_kdf("KDF2(SHA-1)"); SymmetricKey key = kdf->derive_key(20, master_key, "cipher key"); + SymmetricKey mac_key = kdf->derive_key(20, master_key, "hmac key"); + InitializationVector iv = kdf->derive_key(8, master_key, "cipher iv"); Pipe pipe(new Fork( @@ -50,7 +55,7 @@ int main() ) ); - outfile.write((const char*)the_salt.begin(), the_salt.size()); + outfile.write((const char*)salt.begin(), salt.size()); pipe.start_msg(); infile >> pipe; diff --git a/doc/examples/passhash.cpp b/doc/examples/passhash.cpp index 24f7ff674..8c50de072 100644 --- a/doc/examples/passhash.cpp +++ b/doc/examples/passhash.cpp @@ -55,18 +55,26 @@ int main(int argc, char* argv[]) return 0; } +const u32bit SALT_BYTES = 8; // 64 bits of salt +const u32bit PBKDF_OUTPUT_LEN = 16; // 128 bits output +const u32bit KDF_ITERATIONS = 100000; + std::string password_hash(const std::string& pass, RandomNumberGenerator& rng) { PKCS5_PBKDF2 kdf(new HMAC(new SHA_160)); - kdf.set_iterations(10000); - kdf.new_random_salt(rng, 6); // 48 bits + SecureVector<byte> salt(SALT_BYTES); + rng.randomize(&salt[0], salt.size()); + + // Encode the salt plus 96 bits of PBKDF2 output Pipe pipe(new Base64_Encoder); pipe.start_msg(); - pipe.write(kdf.current_salt()); - pipe.write(kdf.derive_key(12, pass).bits_of()); + pipe.write(salt); + pipe.write(kdf.derive_key(PBKDF_OUTPUT_LEN, pass, + &salt[0], salt.size(), + KDF_ITERATIONS).bits_of()); pipe.end_msg(); return pipe.read_all_as_string(); @@ -81,12 +89,15 @@ bool password_hash_ok(const std::string& pass, const std::string& hash) SecureVector<byte> hash_bin = pipe.read_all(); - PKCS5_PBKDF2 kdf(new HMAC(new SHA_160)); + if(hash_bin.size() != (PBKDF_OUTPUT_LEN + SALT_BYTES)) + return false; - kdf.set_iterations(10000); - kdf.change_salt(hash_bin, 6); + PKCS5_PBKDF2 kdf(new HMAC(new SHA_160)); - SecureVector<byte> cmp = kdf.derive_key(12, pass).bits_of(); + SecureVector<byte> cmp = kdf.derive_key( + PBKDF_OUTPUT_LEN, pass, + &hash_bin[0], SALT_BYTES, + KDF_ITERATIONS).bits_of(); return same_mem(cmp.begin(), hash_bin.begin() + 6, 12); } diff --git a/doc/examples/row_encryptor.cpp b/doc/examples/row_encryptor.cpp index 17f44ce7b..7c234105d 100644 --- a/doc/examples/row_encryptor.cpp +++ b/doc/examples/row_encryptor.cpp @@ -66,10 +66,9 @@ void Row_Encryptor::init(const std::string& passphrase) { std::auto_ptr<S2K> s2k(get_s2k("PBKDF2(SHA-160)")); - s2k->set_iterations(10000); - s2k->change_salt(&s2k_salt[0], s2k_salt.size()); - - SecureVector<byte> key = s2k->derive_key(32, passphrase).bits_of(); + SecureVector<byte> key = s2k->derive_key(32, passphrase, + &s2k_salt[0], s2k_salt.size(), + 10000).bits_of(); /* Save pointers to the EAX objects so we can change the IV as needed |