Merge pull request #15 from BrownBear2/phoenix
added missing files: romix.[hc]pp
This commit is contained in:
commit
5f36e9acac
2 changed files with 179 additions and 0 deletions
80
include/fc/crypto/romix.hpp
Normal file
80
include/fc/crypto/romix.hpp
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Most of this file has been ported from EncryptionUtils.cpp from BitcoinArmory:
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Copyright(C) 2011-2013, Armory Technologies, Inc. //
|
||||||
|
// Distributed under the GNU Affero General Public License (AGPL v3) //
|
||||||
|
// See LICENSE or http://www.gnu.org/licenses/agpl.html //
|
||||||
|
// //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// For the KDF:
|
||||||
|
//
|
||||||
|
// This technique is described in Colin Percival's paper on memory-hard
|
||||||
|
// key-derivation functions, used to create "scrypt":
|
||||||
|
//
|
||||||
|
// http://www.tarsnap.com/scrypt/scrypt.pdf
|
||||||
|
//
|
||||||
|
// The goal is to create a key-derivation function that can force a memory
|
||||||
|
// requirement on the thread applying the KDF. By picking a sequence-length
|
||||||
|
// of 1,000,000, each thread will require 32 MB of memory to compute the keys,
|
||||||
|
// which completely disarms GPUs of their massive parallelization capabilities
|
||||||
|
// (for maximum parallelization, the kernel must use less than 1-2 MB/thread)
|
||||||
|
//
|
||||||
|
// Even with less than 1,000,000 hashes, as long as it requires more than 64
|
||||||
|
// kB of memory, a GPU will have to store the computed lookup tables in global
|
||||||
|
// memory, which is extremely slow for random lookup. As a result, GPUs are
|
||||||
|
// no better (and possibly much worse) than a CPU for brute-forcing the passwd
|
||||||
|
//
|
||||||
|
// This KDF is actually the ROMIX algorithm described on page 6 of Colin's
|
||||||
|
// paper. This was chosen because it is the simplest technique that provably
|
||||||
|
// achieves the goal of being secure, and memory-hard.
|
||||||
|
//
|
||||||
|
// The computeKdfParams method well test the speed of the system it is running
|
||||||
|
// on, and try to pick the largest memory-size the system can compute in less
|
||||||
|
// than 0.25s (or specified target).
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// NOTE: If you are getting an error about invalid argument types, from python,
|
||||||
|
// it is usually because you passed in a BinaryData/Python-string instead
|
||||||
|
// of a SecureBinaryData object
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace fc {
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// A memory-bound key-derivation function -- uses a variation of Colin
|
||||||
|
// Percival's ROMix algorithm: http://www.tarsnap.com/scrypt/scrypt.pdf
|
||||||
|
//
|
||||||
|
// The computeKdfParams method takes in a target time, T, for computation
|
||||||
|
// on the computer executing the test. The final KDF should take somewhere
|
||||||
|
// between T/2 and T seconds.
|
||||||
|
class romix
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
romix(u_int32_t memReqts, u_int32_t numIter, std::string salt);
|
||||||
|
|
||||||
|
std::string deriveKey_OneIter(std::string const & password);
|
||||||
|
std::string deriveKey(std::string const & password);
|
||||||
|
|
||||||
|
private:
|
||||||
|
u_int32_t hashOutputBytes_;
|
||||||
|
u_int32_t kdfOutputBytes_; // size of final key data
|
||||||
|
|
||||||
|
u_int32_t memoryReqtBytes_;
|
||||||
|
u_int32_t sequenceCount_;
|
||||||
|
std::string salt_; // prob not necessary amidst numIter, memReqts
|
||||||
|
// but I guess it can't hurt
|
||||||
|
|
||||||
|
u_int32_t numIterations_; // We set the ROMIX params for a given memory
|
||||||
|
// req't. Then run it numIter times to meet
|
||||||
|
// the computation-time req't
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
99
src/crypto/romix.cpp
Normal file
99
src/crypto/romix.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Most of this file has been ported from EncryptionUtils.cpp from BitcoinArmory:
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// //
|
||||||
|
// Copyright(C) 2011-2013, Armory Technologies, Inc. //
|
||||||
|
// Distributed under the GNU Affero General Public License (AGPL v3) //
|
||||||
|
// See LICENSE or http://www.gnu.org/licenses/agpl.html //
|
||||||
|
// //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#include <fc/crypto/sha256.hpp>
|
||||||
|
#include <fc/crypto/sha512.hpp>
|
||||||
|
#include <fc/crypto/romix.hpp>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace fc
|
||||||
|
{
|
||||||
|
romix::romix( u_int32_t memReqts, u_int32_t numIter, std::string salt ) :
|
||||||
|
hashOutputBytes_( 64 ),
|
||||||
|
kdfOutputBytes_( 32 )
|
||||||
|
{
|
||||||
|
memoryReqtBytes_ = memReqts;
|
||||||
|
sequenceCount_ = memoryReqtBytes_ / hashOutputBytes_;
|
||||||
|
numIterations_ = numIter;
|
||||||
|
salt_ = salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string romix::deriveKey_OneIter( std::string const & password )
|
||||||
|
{
|
||||||
|
static fc::sha512 sha512;
|
||||||
|
|
||||||
|
// Concatenate the salt/IV to the password
|
||||||
|
std::string saltedPassword = password + salt_;
|
||||||
|
|
||||||
|
// Prepare the lookup table
|
||||||
|
char *lookupTable_ = new char[memoryReqtBytes_];
|
||||||
|
u_int32_t const HSZ = hashOutputBytes_;
|
||||||
|
|
||||||
|
// First hash to seed the lookup table, input is variable length anyway
|
||||||
|
fc::sha512 hash = sha512.hash(saltedPassword);
|
||||||
|
memcpy(lookupTable_, &hash, HSZ);
|
||||||
|
|
||||||
|
// Compute <sequenceCount_> consecutive hashes of the passphrase
|
||||||
|
// Every iteration is stored in the next 64-bytes in the Lookup table
|
||||||
|
for( u_int32_t nByte = 0; nByte < memoryReqtBytes_ - HSZ; nByte += HSZ )
|
||||||
|
{
|
||||||
|
// Compute hash of slot i, put result in slot i+1
|
||||||
|
fc::sha512 hash = sha512.hash(lookupTable_ + nByte, HSZ);
|
||||||
|
memcpy(lookupTable_ + nByte + HSZ, &hash, HSZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupTable should be complete, now start lookup sequence.
|
||||||
|
// Start with the last hash from the previous step
|
||||||
|
std::string X(lookupTable_ + memoryReqtBytes_ - HSZ, HSZ);
|
||||||
|
std::string Y(HSZ, '0');
|
||||||
|
|
||||||
|
// We "integerize" a hash value by taking the last 4 bytes of
|
||||||
|
// as a u_int32_t, and take modulo sequenceCount
|
||||||
|
u_int64_t* X64ptr = (u_int64_t*)(X.data());
|
||||||
|
u_int64_t* Y64ptr = (u_int64_t*)(Y.data());
|
||||||
|
u_int64_t* V64ptr = NULL;
|
||||||
|
u_int32_t newIndex;
|
||||||
|
u_int32_t const nXorOps = HSZ / sizeof(u_int64_t);
|
||||||
|
|
||||||
|
// Pure ROMix would use sequenceCount_ for the number of lookups.
|
||||||
|
// We divide by 2 to reduce computation time RELATIVE to the memory usage
|
||||||
|
// This still provides suffient LUT operations, but allows us to use more
|
||||||
|
// memory in the same amount of time (and this is the justification for
|
||||||
|
// the scrypt algorithm -- it is basically ROMix, modified for more
|
||||||
|
// flexibility in controlling compute-time vs memory-usage).
|
||||||
|
u_int32_t const nLookups = sequenceCount_ / 2;
|
||||||
|
for(u_int32_t nSeq=0; nSeq<nLookups; nSeq++)
|
||||||
|
{
|
||||||
|
// Interpret last 4 bytes of last result (mod seqCt) as next LUT index
|
||||||
|
newIndex = *(u_int32_t*)(X.data()+HSZ-4) % sequenceCount_;
|
||||||
|
|
||||||
|
// V represents the hash result at <newIndex>
|
||||||
|
V64ptr = (u_int64_t*)(lookupTable_ + HSZ * newIndex);
|
||||||
|
|
||||||
|
// xor X with V, and store the result in X
|
||||||
|
for(u_int32_t i = 0; i < nXorOps; i++)
|
||||||
|
*(Y64ptr + i) = *(X64ptr + i) ^ *(V64ptr + i);
|
||||||
|
|
||||||
|
// Hash the xor'd data to get the next index for lookup
|
||||||
|
fc::sha512 hash = sha512.hash(Y.data(), HSZ);
|
||||||
|
X.assign(hash.data(), HSZ);
|
||||||
|
}
|
||||||
|
// Truncate the final result to get the final key
|
||||||
|
delete lookupTable_;
|
||||||
|
return X.substr(0, kdfOutputBytes_);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string romix::deriveKey( std::string const & password )
|
||||||
|
{
|
||||||
|
std::string masterKey(password);
|
||||||
|
for(u_int32_t i=0; i<numIterations_; i++)
|
||||||
|
masterKey = deriveKey_OneIter(masterKey);
|
||||||
|
|
||||||
|
return masterKey;
|
||||||
|
}
|
||||||
|
} // namespace fc
|
||||||
Loading…
Reference in a new issue