This commit is contained in:
2025-11-24 14:19:51 +05:30
commit f5c1412b28
6734 changed files with 1527575 additions and 0 deletions

View File

@ -0,0 +1,45 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_SECURE_COMPARE_HPP
#define CAT_SECURE_COMPARE_HPP
#include <cat/Platform.hpp>
namespace cat {
// Binary comparison function that is resistant to timing attack
// Note that memcmp() and strcmp() are both vulnerable
// Returns true if they are equal
bool SecureEqual(const void *A, const void *B, int bytes);
} // namespace cat
#endif // SECURE_COMPARE_HPP

View File

@ -0,0 +1,70 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_COOKIE_JAR_HPP
#define CAT_COOKIE_JAR_HPP
#include <cat/crypt/rand/Fortuna.hpp>
namespace cat {
class CookieJar
{
static const int EXPIRE_TIME = 4000; // ms
static const int BIN_COUNT = 16; // power of 2
static const int BIN_TIME = EXPIRE_TIME / BIN_COUNT;
static const int BIN_MASK = BIN_COUNT - 1;
u32 key[16];
u32 Salsa6(u32 *x);
u32 Hash(u32 ip, u16 port, u32 epoch);
u32 Hash(const void *address_info, int bytes, u32 epoch);
u32 GetEpoch();
u32 ReconstructEpoch(u32 cookie);
public:
// Initialize to a random 512-bit key on startup
void Initialize(FortunaOutput *csprng);
// Thread-safe and lock-free
u32 Generate(u32 ip, u16 port);
u32 Generate(const void *address_info, int bytes); // bytes <= 48
// Thread-safe and lock-free
bool Verify(u32 ip, u16 port, u32 cookie);
bool Verify(const void *address_info, int bytes, u32 cookie); // bytes <= 48
};
} // namespace cat
#endif // CAT_COOKIE_JAR_HPP

View File

@ -0,0 +1,87 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
HMAC-MD5 is still secure despite the ease of producing collisions in MD5.
See Mihir Bellare paper "New Proofs for NMAC and HMAC: Security without Collision-Resistance" (June 2006)
Using HMAC construction:
HMAC(x) = h(k || p1 || h(k || p2 || x))
h() = MD5 hash
p1,p2 = distinct padding to bring k up to the block size
p1 = 0x36 repeated, p2 = 0x5c repeated
Diverges from usual implementation by using little-endian rather than big-endian input
*/
#ifndef HMAC_MD5_HPP
#define HMAC_MD5_HPP
#include <cat/crypt/hash/ICryptHash.hpp>
namespace cat {
class CAT_EXPORT HMAC_MD5 : public ICryptHash
{
protected:
static const int DIGEST_BYTES = 16;
static const int WORK_BYTES = 64; // bytes in one block
static const int WORK_WORDS = WORK_BYTES / sizeof(u32);
u32 CachedInitialState[4]; // Cached state for H(K||inner padding)
u32 CachedFinalState[4]; // Cached state for H(K||outer padding)
u64 byte_counter;
u32 State[4];
u8 Work[WORK_BYTES];
int used_bytes;
void HashComputation(const void *message, int blocks, u32 *NextState);
// Unsupported modes
bool BeginKey(int /*bits*/) { return false; }
bool BeginKDF() { return false; }
bool BeginPRNG() { return false; }
public:
~HMAC_MD5();
bool SetKey(ICryptHash *parent);
void RekeyFromMD5(HMAC_MD5 *parent);
bool BeginMAC();
void Crunch(const void *message, int bytes);
void End();
// TODO: Strengthening is not supported right now
void Generate(void *out, int bytes, int strengthening_rounds = 0);
};
} // namespace cat
#endif // HMAC_MD5_HPP

View File

@ -0,0 +1,79 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
// 06/15/09 began
#ifndef CAT_I_CRYPT_HASH_HPP
#define CAT_I_CRYPT_HASH_HPP
#include <cat/Platform.hpp>
#include <cstring>
namespace cat {
// Cryptographic hash functions of any size will derive from ICryptoHash and implement its public methods
class CAT_EXPORT ICryptHash
{
protected:
int digest_bytes;
public:
virtual ~ICryptHash() {}
// Returns the number of bytes in a message digest produced by this hash
CAT_INLINE int GetDigestByteCount() { return digest_bytes; }
CAT_INLINE void CrunchString(const char *s) { Crunch(s, (int)std::strlen(s) + 1); }
public:
// Begin a new key
virtual bool BeginKey(int bits) = 0;
// Start from an existing key
virtual bool SetKey(ICryptHash *parent) = 0;
// Begin hash function in MAC, KDF, or PRNG mode
virtual bool BeginMAC() = 0;
virtual bool BeginKDF() = 0;
virtual bool BeginPRNG() = 0;
// Crunch some message bytes
virtual void Crunch(const void *message, int bytes) = 0;
// Finalize the hash and prepare to generate output
virtual void End() = 0;
// Extended hash output mode
virtual void Generate(void *out, int bytes, int strengthening_rounds = 0) = 0;
};
} // namespace cat
#endif // CAT_I_CRYPT_HASH_HPP

View File

@ -0,0 +1,108 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Bruce Schneier's SHA-3 candidate Skein hash function
http://www.skein-hash.info/
*/
#ifndef CAT_SKEIN_HPP
#define CAT_SKEIN_HPP
#include <cat/crypt/hash/ICryptHash.hpp>
namespace cat {
// Base class for various versions of Skein
class CAT_EXPORT Skein : public ICryptHash
{
protected:
// Tweak word 1 bit field starting positions
static const int T1_POS_TREE_LVL = 112-64; // bits 112..118 : level in hash tree
static const int T1_POS_BIT_PAD = 119-64; // bit 119 : partial final input byte
static const int T1_POS_BLK_TYPE = 120-64; // bits 120..125 : type field
static const int T1_POS_FIRST = 126-64; // bits 126 : first block flag
static const int T1_POS_FINAL = 127-64; // bit 127 : final block flag
// Tweak word 1 bit field masks
static const u64 T1_MASK_FIRST = (u64)1 << T1_POS_FIRST;
static const u64 T1_MASK_FINAL = (u64)1 << T1_POS_FINAL;
static const u64 T1_MASK_BIT_PAD = (u64)1 << T1_POS_BIT_PAD;
static const u64 T1_MASK_TREE_LVL = (u64)0x7F << T1_POS_TREE_LVL;
static const u64 T1_MASK_BLK_TYPE = (u64)63 << T1_POS_BLK_TYPE;
static const int BLK_TYPE_KEY = 0; // key, for MAC and KDF
static const int BLK_TYPE_CFG = 4; // configuration block
static const int BLK_TYPE_PERS = 8; // personalization string
static const int BLK_TYPE_PK = 12; // public key (for digital signature hashing)
static const int BLK_TYPE_KDF = 16; // key identifier for KDF
static const int BLK_TYPE_NONCE = 20; // nonce for PRNG
static const int BLK_TYPE_MSG = 48; // message processing
static const int BLK_TYPE_OUT = 63; // output stage
static const u32 ID_STRING_LE = 0x33414853;
static const u32 SKEIN_VERSION = 1;
static const u64 SCHEMA_VER = ((u64)SKEIN_VERSION << 32) | ID_STRING_LE;
static const int MAX_BITS = 512;
static const int MAX_WORDS = MAX_BITS / 64;
static const int MAX_BYTES = MAX_BITS / 8;
u64 Tweak[2];
u64 State[MAX_WORDS];
u8 Work[MAX_BYTES];
int used_bytes, digest_words;
u64 output_block_counter;
bool output_prng_mode;
typedef void (Skein::*HashComputation)(const void *message, int blocks, u32 byte_count, u64 *NextState);
void HashComputation256(const void *message, int blocks, u32 byte_count, u64 *NextState);
void HashComputation512(const void *message, int blocks, u32 byte_count, u64 *NextState);
HashComputation hash_func;
void GenerateInitialState(int bits);
public:
~Skein();
bool BeginKey(int bits);
bool SetKey(ICryptHash *parent);
bool BeginMAC();
bool BeginKDF();
bool BeginPRNG();
void Crunch(const void *message, int bytes);
void End();
void Generate(void *out, int bytes, int strengthening_rounds = 0);
};
} // namespace cat
#endif // CAT_SKEIN_HPP

View File

@ -0,0 +1,193 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_PASSWORDS_HPP
#define CAT_PASSWORDS_HPP
#include <cat/rand/IRandom.hpp>
namespace cat {
/*
Why are passwords needed?
Passwords allow the client identity to be proved to the server. Usually
the server identity is already proven by a secure connection, but the
client identity is unproven until a password is provided.
This could be done using signatures with two-way authentication in the
secure connection establishment, however this is very slow and roughly
doubles the work a server has to do to accept a connection.
Passwords seem simple to implement at first but then you start imagining all
the "what ifs":
What if the password database is stolen?
The user passwords had better not be stored in plaintext!
So they must be hashed.
What if two users have the same password?
They would look the same hashed, so hash in the name with the password.
What if name:password for two users is "me : pass" and "m : epass" ?
They would look the same hashed, so insert a separator between the two
before hashing, as in "me\r\npass" instead of just hashing "mepass".
What if someone tries to crack the passwords?
The password hash should be strengthened to make it harder to crack,
by applying the hash function repeatedly. This does reduce the number of
bits of entropy due to non-ideal hash function collisions, but this small
effect is offset by the added difficulty to crack the password.
What if the user has a common name like "dave"?
The password hash should be salted to avoid falling to rainbow tables.
What if the attacker tries to use the stolen password hash to log into the server?
The client provides a hash pre-image during login, so the attacker would need
to reverse the hash to do this, which is harder than cracking the password.
What if an attacker has a way to actively or passively listen to login attempts?
Then they would be able to steal the hash pre-image which would offer some
amount of text password protection but would allow the attacker to log in
as the user without knowing the password.
Rainbow tables can be used to crack the pre-image to determine the password.
If the connection is secure then the client has verified the server identity
and these attacks are only possible through software bugs in the server.
Protocol Description:
H: 256-bit hash function Skein-256
c: Client
s: Server
X: Strengthening factor
Protocol 1: Creating a User Account
c: Choose a name N
c: Choose a password P
c: Calculate I = H("N\r\nP") strengthened X times
c2s CREATE_ACCOUNT || N || I (32 by)
s: Verify account is not taken, then creates an account in the database
s: Generate a 32-bit random salt S
s: Calculate J = H(S || I)
s: Store N, J and S in the account
s2c ACCOUNT_CREATE_SUCCESS
Protocol 2: Logging into a User Account
c: Choose a name N
c: Choose a password P
c: Calculate I = H("N\r\nP") strengthened X times
c2s LOGIN_REQUEST || N || I (32 by)
s: Verify user name exists in account database
s: Retrieve J, S from the account database
s: Calculate J' = H(S || I)
s: Verify J = J'
s2c LOGIN_SUCCESS
What if the user creates an account through a website?
The server-side script on the website should execute a program that runs
Protocol 1 to create the user account.
Why use this sort of password authentication + Tunnel instead of SRP?
Performance. The Tunnel key agreement takes much less CPU time than SRP
and authenticates the server, which allows this protocol to be run without
any disadvantages.
*/
class PasswordBase
{
public:
static const int HASH_BITS = 256;
static const int HASH_BYTES = HASH_BITS / 8;
static const int STRENGTHENING_FACTOR = 1000;
};
class PasswordCreator : public PasswordBase
{
public:
// Hash a password for transmission to the server during account creation or login
// NOTE: You should make the name all lowercase before passing it in
// Returns false on failure
bool HashPassword(const void *in_name, int name_bytes,
const void *in_password, int password_bytes,
void *out_hash /* 32 bytes */);
// This version accepts non-unicode C strings with nul-terminators
// It converts the name to lowercase internally
bool HashPasswordString(const char *in_name,
const char *in_password,
void *out_hash /* 32 bytes */);
};
class PasswordVerifier : public PasswordBase
{
public:
// Salt a hash for storage in the account database during account creation
// Generates a 32-bit salt and a 256-bit hash for storage
// Returns false on failure
bool SaltHash(IRandom *prng,
const void *in_hash /* 32 bytes */,
void *out_salted_hash /* 32 bytes */,
u32 *out_salt /* 4 bytes */);
// Given the stored salted hash and salt, verify the client's password hash during login
// Returns false if password is incorrect
bool VerifyHash(const void *in_hash /* 32 bytes */,
const void *in_salted_hash /* 32 bytes */,
u32 in_salt /* 4 bytes */);
};
} // namespace cat
#endif // CAT_PASSWORDS_HPP

View File

@ -0,0 +1,222 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
Based on Fortuna algorithm from "Practical Cryptography" section 10.3
Published 2003 by Niels Ferguson and Bruce Schneier
Fortuna supplements the operating system (OS) pseudo-random number generator (PRNG)
by incorporating additional entropy into the seeds that the OS provides.
Modified for use with Skein in PRNG mode, sharing the strengths of both algorithms.
My implementation of Fortuna (call it Cat For Tuna) has the following components:
+ Entropy Pools
+ 32 pools numbered 0..31
+ Implemented as 32 instances of the 256-bit Skein hash
+ Entropy hashed into pools in a round-robin fashion
+ Scheme allows for recovery against attacker with knowledge of some sources
+ Entropy Sources
+ Uses best OS random number generator
+ And a variable number of OS-dependent entropy sources
+ Mainly cycle counts and other timing information
+ Output Generator
+ Implemented as a 512-bit Skein hash of some combination of the entropy pools
+ The output is produced by the PRNG mode of Skein keyed by the pools
+ Hashing all of these pools together and keying the output is called "seeding"
+ Reseeded after sufficient entropy has been collected in pool 0
+ Pool 0 is always used for reseeding
+ Reseed X uses pools numbered by the 1-bits in X (except MSB)
+ Previous seed keys the next seed
+ Diverges from normal Fortuna due to use of Skein instead of a block cipher
+ Reseeds only once every ~512 seconds
+ Does not have a limit of 2^16 output blocks
+ Skein-PRNG is guaranteed sufficient security properties anyway
The Fortuna algorithm is broken up into two objects for efficient thread-safety:
+ FortunaFactory
+ Must be initialized on startup and shut down on shutdown
+ Spawns a thread to periodically collect additional entropy
+ Can create FortunaOutput objects
+ FortunaOutput
+ Reseeds based on master seed in the FortunaFactory
+ Reseeds after master seed is updated
+ Provides a unique random stream for each thread that uses Fortuna
*/
#ifndef CAT_FOR_TUNA_HPP
#define CAT_FOR_TUNA_HPP
#include <cat/rand/IRandom.hpp>
#include <cat/crypt/hash/Skein.hpp>
#include <cat/Singleton.hpp>
#include <cat/threads/Mutex.hpp>
// Defining CAT_NO_ENTROPY_THREAD will remove dependencies on pthreads and not
// run a thread to collect more entropy. This is recommended for low-power targets
// and other systems where no thread library is available
#if defined(CAT_OS_WINDOWS_CE)
# define CAT_NO_ENTROPY_THREAD
#endif
#if defined(CAT_OS_WINDOWS)
# include <cat/port/WindowsInclude.hpp>
# include <wincrypt.h>
#endif
#if !defined(CAT_NO_ENTROPY_THREAD)
# include <cat/threads/Thread.hpp>
# include <cat/threads/WaitableFlag.hpp>
#endif
namespace cat {
class FortunaOutput;
class FortunaFactory;
// Factory for constructing FortunaOutput objects
class FortunaFactory : public Singleton<FortunaFactory>
#if !defined(CAT_NO_ENTROPY_THREAD)
, public Thread
#endif
{
CAT_SINGLETON(FortunaFactory)
{
_initialized = false;
}
Mutex _lock;
friend class FortunaOutput;
#if defined(CAT_OS_WINDOWS) && !defined(CAT_OS_WINDOWS_CE)
typedef LONG (WINAPI *PtNtQuerySystemInformation)(
int SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
HANDLE CurrentProcess;
HMODULE NTDLL;
PtNtQuerySystemInformation NtQuerySystemInformation;
HCRYPTPROV hCryptProv;
#elif defined(CAT_OS_WINDOWS_CE)
HCRYPTPROV hCryptProv;
#elif defined(CAT_OS_LINUX)
int urandom_fd;
#endif
#if !defined(CAT_NO_ENTROPY_THREAD)
static const int ENTROPY_THREAD_KILL_TIMEOUT = 10000; // 10 seconds
WaitableFlag _kill_flag;
bool ThreadFunction(void *param);
#endif
protected:
static const int ENTROPY_POOLS = 32; // Setting this higher would break something
static const int POOL_BITS = 512;
static const int POOL_BYTES = POOL_BITS / 8;
static const int POOL_QWORDS = POOL_BYTES / sizeof(u64);
static u32 MasterSeedRevision; // Should not roll over for 13 years if incremented once every RESEED_MIN_TIME
static Skein MasterSeed;
bool _initialized;
u32 reseed_counter;
Skein Pool[ENTROPY_POOLS];
bool Reseed();
void GetNextKey(FortunaOutput *output);
bool InitializeEntropySources();
void PollInvariantSources(int pool);
void PollSlowEntropySources(int pool);
void PollFastEntropySources(int pool);
void ShutdownEntropySources();
public:
// Start the entropy generator
bool Initialize();
// Stop the entropy generator
void Shutdown();
// Create a new Fortuna object
static FortunaOutput *Create();
};
// LoopThread-safe output object for Fortuna
class FortunaOutput : public IRandom
{
friend class FortunaFactory;
static const int OUTPUT_CACHE_BYTES = FortunaFactory::POOL_BYTES * 8; // Arbitrary
u32 thread_id, SeedRevision;
Skein OutputHash;
u8 CachedRandomBytes[OUTPUT_CACHE_BYTES];
int used_bytes;
void Reseed();
FortunaOutput();
FortunaOutput(FortunaOutput&) {}
FortunaOutput &operator=(FortunaOutput &) { return *this; }
public:
~FortunaOutput();
// Generate a 32-bit random number
u32 Generate();
// Generate a variable number of random bytes
void Generate(void *buffer, int bytes);
};
} // namespace cat
#endif // CAT_FOR_TUNA_HPP

View File

@ -0,0 +1,145 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
/*
The ChaCha cipher is a symmetric stream cipher based on Salsa20.
http://cr.yp.to/chacha.html
*/
#ifndef CAT_CHACHA_HPP
#define CAT_CHACHA_HPP
#include <cat/Platform.hpp>
namespace cat {
/*
To initialize the ChaCha cipher, you must specify a 256-bit key.
Code example:
ChaChaKey cck;
char key[32]; // fill key here
cck.Key(key, sizeof(key));
Before each encryption or decryption with the ChaCha cipher,
a 64-bit Initialization Vector (IV) must be specified. Every
time a message is encrypted, the IV must be incremented by 1.
The IV is then sent along with the encrypted message.
Encryption code example:
char message[19], ciphertext[19]; // message filled here
u64 iv = 125125;
u64 message_iv = iv;
iv = iv + 1;
ChaChaOutput cco(cck, message_iv);
cco.Crypt(message, ciphertext, sizeof(ciphertext));
Decryption code example:
char ciphertext[19], decrypted[19]; // ciphertext filled here
ChaChaOutput cco(cck, message_iv);
cco.Crypt(ciphertext, decrypted, sizeof(decrypted));
Sending all 8 bytes of the IV in every packet is not necessary.
Instead, only a few of the low bits of the IV need to be sent,
if the IV is incremented by 1 each time.
How many? It depends on how many messages can get lost.
If < 32768 messages can get lost in a row, then CAT_IV_BITS = 16 (default)
I have provided a function to handle rollover/rollunder of the IV,
which also works if the same IV is sent twice for some reason.
It needs to know how many of the low bits are sent across, so be sure
to change CAT_IV_BITS in this header if you send more or less than 16.
Code example:
u64 last_accepted_iv;
u32 new_iv_low_bits;
u64 new_iv = ChaCha::ReconstructIV(last_accepted_iv, new_iv_low_bits);
-------------------------READ THIS BEFORE USING--------------------------
Never use the same IV twice.
Otherwise: An attacker can recover the plaintext without the key.
Never use the same key twice.
Otherwise: An attacker can recover the plaintext without the key.
If you have two hosts talking to eachother securely with ChaCha encryption,
then be sure that each host is encrypting with a DIFFERENT key.
Otherwise: An attacker can recover the plaintext without the key.
Remember that an attacker can impersonate the remote computer, so be
sure not to accept the new IV until the message authentication code has
been verified if your protocol uses a message authentication code (MAC).
Otherwise: An attacker could desynchronize the IVs.
*/
//// ChaChaKey
class CAT_EXPORT ChaChaKey
{
friend class ChaChaOutput;
u32 state[16];
public:
~ChaChaKey();
// Key up to 384 bits
void Set(const void *key, int bytes);
};
//// ChaChaOutput
class CAT_EXPORT ChaChaOutput
{
u32 state[16];
void GenerateKeyStream(u32 *out);
public:
ChaChaOutput(const ChaChaKey &key, u64 iv);
~ChaChaOutput();
// Message with any number of bytes
void Crypt(const void *in, void *out, int bytes);
};
} // namespace cat
#endif // CAT_CHACHA_HPP

View File

@ -0,0 +1,148 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_AUTHENTICATED_ENCRYPTION_HPP
#define CAT_AUTHENTICATED_ENCRYPTION_HPP
#include <cat/crypt/symmetric/ChaCha.hpp>
#include <cat/crypt/hash/Skein.hpp>
#include <cat/crypt/hash/HMAC_MD5.hpp>
namespace cat {
/*
Tunnel Authenticated Encryption "Calico" protocol:
Run after the Key Agreement protocol completes.
Uses a 1024-bit anti-replay sliding window, suitable for Internet file transfer over UDP.
Cipher: 12-round ChaCha with 256-bit or 384-bit keys
KDF: Key derivation function (Skein)
MAC: 64-bit truncated HMAC-MD5
IV: Initialization vector incrementing by 1 each time
c2sMKey = KDF(k) { "upstream-MAC" }
s2cMKey = KDF(k) { "downstream-MAC" }
c2sEKey = KDF(k) { "upstream-ENC" }
s2cEKey = KDF(k) { "downstream-ENC" }
c2sIV = KDF(k) { "upstream-IV" }
s2cIV = KDF(k) { "downstream-IV" }
To transmit a message, the client calculates a MAC with the c2sMKey of the IV concatenated with
the plaintext message and then appends the 8-byte MAC and low 3 bytes of the IV to the message,
which is encrypted using the c2sEKey and the IV.
c2s Encrypt(c2sEKey) { message || MAC(c2sMKey) { full-iv-us||message } } || Obfuscated { trunc-iv-us }
encrypted { MESSAGE(X) MAC(8by) } IV(3by) = 11 bytes overhead at end of packet
To transmit a message, the server calculates a MAC with the s2cMKey of the IV concatenated with
the plaintext message and then appends the 8-byte MAC and low 3 bytes of the IV to the message,
which is encrypted using the s2cEKey and the IV.
s2c Encrypt(s2cEKey) { message || MAC(s2cMKey) { full-iv-ds||message } } || Obfuscated { trunc-iv-ds }
encrypted { MESSAGE(X) MAC(8by) } IV(3by) = 11 bytes overhead at end of packet
The full 64-bit IVs are initialized to c2sIV and s2cIV, and the first one sent is IV+1.
*/
class KeyAgreementResponder;
class KeyAgreementInitiator;
// This class is NOT THREAD-SAFE.
class CAT_EXPORT AuthenticatedEncryption
{
friend class KeyAgreementResponder;
friend class KeyAgreementInitiator;
bool _is_initiator, _accept_out_of_order;
Skein key_hash;
HMAC_MD5 local_mac_key, remote_mac_key;
ChaChaKey local_cipher_key, remote_cipher_key;
u64 local_iv, remote_iv;
// 1024-bit anti-replay sliding window
static const int BITMAP_BITS = 1024;
static const int BITMAP_WORDS = BITMAP_BITS / 64;
u64 iv_bitmap[BITMAP_WORDS];
public:
CAT_INLINE AuthenticatedEncryption() {}
CAT_INLINE ~AuthenticatedEncryption() {}
// Tunnel overhead bytes
static const int MAC_BYTES = 8;
static const int IV_BYTES = 3;
static const u32 OVERHEAD_BYTES = IV_BYTES + MAC_BYTES;
// IV constants
static const int IV_BITS = IV_BYTES * 8;
static const u32 IV_MSB = (1 << IV_BITS);
static const u32 IV_MASK = (IV_MSB - 1);
static const u32 IV_FUZZ = 0xCA7DCA7D;
protected:
bool SetKey(int KeyBytes, Skein *key, bool is_initiator, const char *key_name);
bool IsValidIV(u64 iv);
void AcceptIV(u64 iv);
public:
// Generate a proof that the local host has the key
bool GenerateProof(u8 *local_proof, int proof_bytes);
// Validate a proof that the remote host has the key
bool ValidateProof(const u8 *remote_proof, int proof_bytes);
public:
void AllowOutOfOrder(bool allowed = true) { _accept_out_of_order = allowed; }
public:
// Overhead is OVERHEAD_BYTES bytes at the end of the packet
// Returns false if the message is invalid. Invalid messages should just be ignored as if they were never received
// buf_bytes: Number of bytes in the buffer, including the overhead
// If Decrypt() returns true, buf_bytes is set to the size of the decrypted message
bool Decrypt(u8 *buffer, u32 &buf_bytes);
// Overhead is OVERHEAD_BYTES bytes at the end of the packet
// buffer_bytes: Number of bytes in the buffer; will return false if buffer size too small
// msg_bytes: Number of bytes in the message, excluding the overhead
// If Encrypt() returns true, msg_bytes is set to the size of the encrypted message
bool Encrypt(u8 *buffer, u32 buffer_bytes, u32 &msg_bytes);
};
} // namespace cat
#endif // CAT_AUTHENTICATED_ENCRYPTION_HPP

View File

@ -0,0 +1,307 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_EASY_HANDSHAKE_HPP
#define CAT_EASY_HANDSHAKE_HPP
#include <cat/crypt/tunnel/KeyAgreementInitiator.hpp>
#include <cat/crypt/tunnel/KeyAgreementResponder.hpp>
#include <cat/crypt/cookie/CookieJar.hpp>
namespace cat {
/*
The EasyHandshake classes implement a simplified version of my Tunnel protocol.
This only works for single-threaded servers and only produces a single authenticated encryption
object for each handshake. Explanations on how to use the library with a multi-threaded
server, and how to use one handshake to secure several TCP streams, are documented in the
comments of these classes.
Over the network, the handshake will look like this:
client --> server : CHALLENGE (64 random-looking bytes)
server --> client : ANSWER (128 random-looking bytes)
client --> server : PROOF (32 random-looking bytes) + first encrypted packet can be appended here
As far as coding goes, the function calls fit into the protocol like this:
server is offline:
During startup, both the client and server should initialize the library.
This is necessary also just for generating keys:
----------------------------------------------------------------
#include <cat/AllTunnel.hpp>
using namespace cat;
if (!EasyHandshake::Initialize())
{
printf("ERROR:Unable to initialize crypto subsystem\n");
}
----------------------------------------------------------------
Generate the server public and private key pairs:
----------------------------------------------------------------
u8 public_key[EasyHandshake::PUBLIC_KEY_BYTES];
u8 private_key[EasyHandshake::PRIVATE_KEY_BYTES];
EasyHandshake temp;
temp.GenerateServerKey(public_key, private_key);
----------------------------------------------------------------
(keys are stored to disk for reading on start-up)
(public key is given to the client somehow)
+ built into the client code
+ provided by a trusted server
server comes online:
----------------------------------------------------------------
ServerEasyHandshake server_handshake;
server_handshake.Initialize(public_key, private_key);
----------------------------------------------------------------
client comes online:
----------------------------------------------------------------
ClientEasyHandshake client_handshake;
client_handshake.Initialize(public_key);
u8 challenge[EasyHandshake::CHALLENGE_BYTES];
client_handshake.GenerateChallenge(challenge);
----------------------------------------------------------------
client --> server : CHALLENGE (64 random-looking bytes)
----------------------------------------------------------------
AuthenticatedEncryption server_e;
u8 answer[EasyHandshake::ANSWER_BYTES];
server_handshake.ProcessChallenge(challenge, answer, &server_e);
----------------------------------------------------------------
server --> client : ANSWER (128 random-looking bytes)
----------------------------------------------------------------
AuthenticatedEncryption client_e;
client_handshake.ProcessAnswer(answer, &client_e);
u8 proof[EasyHandshake::PROOF_BYTES];
client_e.GenerateProof(proof, EasyHandshake::PROOF_BYTES);
----------------------------------------------------------------
Encryption example:
----------------------------------------------------------------
// Example message encryption of "Hello". Note that encryption
// inflates the size of the message by OVERHEAD_BYTES.
const int PLAINTEXT_BYTES = 5;
const int BUFFER_BYTES = \
PLAINTEXT_BYTES + AuthenticatedEncryption::OVERHEAD_BYTES;
int msg_bytes = PLAINTEXT_BYTES;
// Note that it makes room for message inflation
const u8 message[CIPHERTEXT_BYTES] = {
'H', 'e', 'l', 'l', 'o'
};
// Note the second parameter is the size of the buffer, and
// the third parameter will be adjusted to the size of the
// encrypted message:
if (client_e.Encrypt(message, BUFFER_BYTES, msg_bytes))
{
// msg_bytes is now adjusted to be the size of the ciphertext
}
----------------------------------------------------------------
client --> server : PROOF (32 random-looking bytes) + first encrypted packet can be appended here
----------------------------------------------------------------
server_e.ValidateProof(proof, EasyHandshake::PROOF_BYTES);
----------------------------------------------------------------
Decryption example:
----------------------------------------------------------------
int buf_bytes = msg_bytes;
// The second parameter is the number of bytes in the encrypted message
if (server_e.Decrypt(message, buf_bytes))
{
// buf_bytes is now adjusted to be the size of the plaintext
}
----------------------------------------------------------------
During program termination, the client and server should clean up:
----------------------------------------------------------------
EasyHandshake::Shutdown();
----------------------------------------------------------------
NOTES:
Once the authenticated encryption objects are created, if the messages received are always
guaranteed to be in order, then the following flag can be set to make the object reject
packets received out of order, which would indicate tampering:
auth_enc.AllowOutOfOrder(false);
By default the messages are assumed to arrive in any order up to 1024 messages out of order.
The server similarly can encrypt messages the same way the client does in the examples.
Encrypted messages are inflated by 11 random-looking bytes for a MAC and an IV.
Modifications to the code can allow lower overhead if needed.
The EasyHandshake classes are *NOT* THREAD-SAFE.
The AuthenticatedEncryption class is *NOT* THREAD-SAFE. Simultaneously, only ONE thread
can be encrypting messages. And only ONE thread can be decrypting messages. Encryption
and decryption are separate and safe to perform simultaneously.
*/
/*
Common data needed for handshaking
*/
class CAT_EXPORT EasyHandshake
{
protected:
// Normally these would be created per-thread.
// To free memory associated with these objects just delete them.
BigTwistedEdwards *tls_math;
FortunaOutput *tls_csprng;
public:
static const int BITS = 256;
static const int BYTES = BITS / 8;
static const int PUBLIC_KEY_BYTES = BYTES * 2;
static const int PRIVATE_KEY_BYTES = BYTES;
static const int CHALLENGE_BYTES = BYTES * 2; // Packet # 1 in handshake, sent to server
static const int ANSWER_BYTES = BYTES * 4; // Packet # 2 in handshake, sent to client
static const int PROOF_BYTES = BYTES; // Packet # 3 in handshake, sent to server
static const int IDENTITY_BYTES = BYTES * 5; // [optional] Packet # 3 in handshake, sent to server, proves identity of client also
public:
// Demonstrates how to allocate and free the math and prng objects
EasyHandshake();
~EasyHandshake();
public:
static bool Initialize();
static void Shutdown();
public:
// Generate a server (public, private) key pair
// Connecting clients will need to know the public key in order to connect
bool GenerateServerKey(void *out_public_key /* EasyHandshake::PUBLIC_KEY_BYTES */,
void *out_private_key /* EasyHandshake::PRIVATE_KEY_BYTES */);
// Fills the given buffer with a variable number of random bytes
// Returns false on failure
bool GenerateRandomNumber(void *out_num, int bytes);
};
/*
Implements the simple case of a server that performs handshakes with clients
from a single thread. Note that this implementation is not thread-safe.
*/
class CAT_EXPORT ServerEasyHandshake : public EasyHandshake
{
KeyAgreementResponder tun_server;
public:
ServerEasyHandshake();
~ServerEasyHandshake();
// Prepare a cookie jar for hungry consumers
void FillCookieJar(CookieJar *jar);
// Provide the public and private key for the server, previously generated offline
bool Initialize(const void *in_public_key /* EasyHandshake::PUBLIC_KEY_BYTES */,
const void *in_private_key /* EasyHandshake::PRIVATE_KEY_BYTES */);
// Process a client challenge and generate a server answer
// Returns false if challenge was invalid
bool ProcessChallenge(const void *in_challenge /* EasyHandshake::CHALLENGE_BYTES */,
void *out_answer /* EasyHandshake::ANSWER_BYTES */,
AuthenticatedEncryption *auth_enc);
// Validate a client proof of identity
// Returns false if proof was invalid
bool VerifyInitiatorIdentity(const void *in_answer /* EasyHandshake::ANSWER_BYTES */,
const void *in_proof /* EasyHandshake::IDENTITY_BYTES */,
void *out_public_key /* EasyHandshake::PUBLIC_KEY_BYTES */);
};
/*
Implements the simple case of a client that performs handshakes with servers
from a single thread. Note that this implementation is not thread-safe.
*/
class CAT_EXPORT ClientEasyHandshake : public EasyHandshake
{
KeyAgreementInitiator tun_client;
public:
ClientEasyHandshake();
~ClientEasyHandshake();
// Provide the public key for the server, acquired through some secure means
bool Initialize(const void *in_public_key /* EasyHandshake::PUBLIC_KEY_BYTES */);
// (optional) Provide the identity for the client
bool SetIdentity(const void *in_public_key /* EasyHandshake::PUBLIC_KEY_BYTES */,
const void *in_private_key /* EasyHandshake::PRIVATE_KEY_BYTES */);
// Generate a challenge for the server to answer
bool GenerateChallenge(void *out_challenge /* EasyHandshake::CHALLENGE_BYTES */);
// Process a server answer to our challenge
// Returns false if answer was invalid
bool ProcessAnswer(const void *in_answer /* EasyHandshake::ANSWER_BYTES */,
AuthenticatedEncryption *auth_enc);
// Process a server answer to our challenge and provide a proof of client identity
// Returns false if answer was invalid
bool ProcessAnswerWithIdentity(const void *in_answer /* EasyHandshake::ANSWER_BYTES */,
void *out_identity /* EasyHandshake::IDENTITY_BYTES */,
AuthenticatedEncryption *auth_enc);
};
} // namespace cat
#endif // CAT_EASY_HANDSHAKE_HPP

View File

@ -0,0 +1,210 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_KEY_AGREEMENT_HPP
#define CAT_KEY_AGREEMENT_HPP
#include <cat/math/BigTwistedEdwards.hpp>
#include <cat/crypt/rand/Fortuna.hpp>
namespace cat {
/*
Tunnel Key Agreement "Tabby" protocol:
An unauthenticated Diffie-Hellman key agreement protocol with forward secrecy
Immune to active attacks (man-in-the-middle) if server key is known ahead of time
Using Elliptic Curve Cryptography over finite field Fp, p = 2^n - c, c small
Shape of curve: a' * x^2 + y^2 = 1 + d' * x^2 * y^2, a' = -1 (square in Fp)
d' (non square in Fp) -> order of curve = q * cofactor h, order of generator point = q
Curves satisfy MOV conditions and are not anomalous
Point operations performed with Extended Twisted Edwards group laws
See BigTwistedEdwards.hpp for more information
H: Skein-Key, either 256-bit or 512-bit based on security level
MAC: Skein-MAC, keyed from output of H()
Here the protocol initiator is the (c)lient, and the responder is the (s)erver:
s: long-term private key 1 < b < q, long-term public key B = b * G
256-bit security: B = 64 bytes for public key, b = 32 bytes for private key
384-bit security: B = 96 bytes for public key, b = 48 bytes for private key
512-bit security: B = 128 bytes for public key, b = 64 bytes for private key
c: Client already knows the server's public key B before Key Agreement
c: ephemeral private key 1 < a < q, ephemeral public key A = a * G
Initiator Challenge: c2s A
256-bit security: A = 64 bytes
384-bit security: A = 96 bytes
512-bit security: A = 128 bytes
s: validate A, ignore invalid
Invalid A(x,y) would be the additive identity x=0 or any point not on the curve
s: ephemeral private key 1 < y < q, ephemeral public key Y = y * G
Ephemeral key is re-used for several connections before being regenerated
s: hA = h * A
s: random n-bit number r
s: d = H(A,B,Y,r)
Repeat the previous two steps until d >= 1000
s: e = b + d*y (mod q)
s: T = AffineX(e * hA)
s: k = H(d,T)
Responder Answer: s2c Y || r || MAC(k) {"responder proof"}
256-bit security: Y(64by) r(32by) MAC(32by) = 128 bytes
384-bit security: Y(96by) r(48by) MAC(48by) = 192 bytes
512-bit security: Y(128by) r(64by) MAC(64by) = 256 bytes
c: validate Y, ignore invalid
Invalid Y(x,y) would be the additive identity x=0 or any point not on the curve
c: hY = h * Y
c: d = H(A,B,Y,r)
c: Verify d >= 1000
c: T = AffineX(a * hB + d*a * hY)
c: k = H(d,T)
c: validate MAC, ignore invalid
Initiator Proof: c2s MAC(k) {"initiator proof"}
This packet can also include the client's first encrypted message
256-bit security: MAC(32by) = 32 bytes
384-bit security: MAC(48by) = 48 bytes
512-bit security: MAC(64by) = 64 bytes
s: validate MAC, ignore invalid
Notes:
The strategy of this protocol is to perform two EC Diffie-Hellman exchanges,
one with the long-term server key and the second with an ephemeral key that
should be much harder to obtain by an attacker. The resulting two shared
secret points are added together into one point that is used for the key.
It is perfectly acceptable to re-use an ephemeral key for several runs of
the protocol. This means that most of the processing done by the server is
just one point multiplication.
*/
/*
Schnorr signatures:
For signing, the signer reuses its Key Agreement key pair (b,B)
H: Skein-Key, either 256-bit or 512-bit based on security level
To sign a message M, signer computes:
ephemeral secret random 1 < k < q, ephemeral point K = k * G
e = H(M || K)
s = k - b*e (mod q)
This process is repeated until e and s are non-zero
Signature: s2c e || s
256-bit security: e(32by) s(32by) = 64 bytes
384-bit security: e(48by) s(48by) = 96 bytes
512-bit security: e(64by) s(64by) = 128 bytes
To verify a signature:
Check e, s are in the range [1,q-1]
K' = s*G + e*B
e' = H(M || K')
The signature is verified if e == e'
Notes:
K ?= K'
= s*G + e*B
= (k - b*e)*G + e*(b*G)
= k*G - b*e*G + e*b*G
= K
*/
// If CAT_DETERMINISTIC_KEY_GENERATION is undefined, the time to generate a
// key is unbounded, but tends to be 1 try. I think this is a good thing
// because it randomizes the runtime and helps avoid timing attacks
//#define CAT_DETERMINISTIC_KEY_GENERATION
// If CAT_USER_ERROR_CHECKING is defined, the key agreement objects will
// check to make sure that the input parameters are all the right length
// and that the math and prng objects are not null
#define CAT_USER_ERROR_CHECKING
class CAT_EXPORT KeyAgreementCommon
{
public:
static BigTwistedEdwards *InstantiateMath(int bits);
// Math library register usage
static const int ECC_REG_OVERHEAD = 21;
// c: field prime modulus p = 2^bits - C, p = 5 mod 8 s.t. a=-1 is a square in Fp
// d: curve coefficient (yy-xx=1+Dxxyy), not a square in Fp
static const int EDWARD_C_256 = 435;
static const int EDWARD_D_256 = 31720;
static const int EDWARD_C_384 = 2147;
static const int EDWARD_D_384 = 13036;
static const int EDWARD_C_512 = 875;
static const int EDWARD_D_512 = 32;
// Limits on field prime
static const int MAX_BITS = 512;
static const int MAX_BYTES = MAX_BITS / 8;
static const int MAX_LEGS = MAX_BYTES / sizeof(Leg);
protected:
int KeyBits, KeyBytes, KeyLegs;
bool Initialize(int bits);
public:
// Generates an unbiased random key in the range 1 < key < q
void GenerateKey(BigTwistedEdwards *math, IRandom *prng, Leg *key);
};
} // namespace cat
#endif // CAT_KEY_AGREEMENT_HPP

View File

@ -0,0 +1,101 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_KEY_AGREEMENT_INITIATOR_HPP
#define CAT_KEY_AGREEMENT_INITIATOR_HPP
#include <cat/crypt/tunnel/KeyAgreement.hpp>
#include <cat/crypt/tunnel/AuthenticatedEncryption.hpp>
namespace cat {
class CAT_EXPORT KeyAgreementInitiator : public KeyAgreementCommon
{
Leg *B; // Responder's public key (pre-shared with initiator)
Leg *a; // Initiator's private key (kept secret)
Leg *A; // Initiator's public key (shared with responder in Challenge message)
Leg *hB; // h*B
Leg *G_MultPrecomp; // Precomputed table for multiplication
Leg *B_MultPrecomp; // Precomputed table for multiplication
Leg *Y_MultPrecomp; // Precomputed table for multiplication
Leg *A_neutral; // Endian-neutral A
Leg *B_neutral; // Endian-neutral B
// Identity data
Leg *I_private; // Initiator's identity private key
Leg *I_public; // Endian-neutral initiator's identity public key
bool AllocateMemory();
void FreeMemory();
public:
KeyAgreementInitiator();
~KeyAgreementInitiator();
bool Initialize(BigTwistedEdwards *math,
const u8 *responder_public_key, int public_bytes);
// Call after Initialize()
bool SetIdentity(BigTwistedEdwards *math,
const u8 *initiator_public_key, int public_bytes,
const u8 *initiator_private_key, int private_bytes);
public:
bool GenerateChallenge(BigTwistedEdwards *math, FortunaOutput *csprng,
u8 *initiator_challenge, int challenge_bytes);
bool ProcessAnswer(BigTwistedEdwards *math,
const u8 *responder_answer, int answer_bytes,
Skein *key_hash);
// Will fail if SetIdentity() has not been called
bool ProcessAnswerWithIdentity(BigTwistedEdwards *math, FortunaOutput *csprng,
const u8 *responder_answer, int answer_bytes,
Skein *key_hash,
u8 *identity_proof, int proof_bytes);
CAT_INLINE bool KeyEncryption(Skein *key_hash, AuthenticatedEncryption *auth_enc, const char *key_name)
{
return auth_enc->SetKey(KeyBytes, key_hash, true, key_name);
}
// Erase the private key after handshake completes
// Also done as this object is destroyed
void SecureErasePrivateKey();
public:
bool Verify(BigTwistedEdwards *math,
const u8 *message, int message_bytes,
const u8 *signature, int signature_bytes);
};
} // namespace cat
#endif // CAT_KEY_AGREEMENT_INITIATOR_HPP

View File

@ -0,0 +1,97 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_KEY_AGREEMENT_RESPONDER_HPP
#define CAT_KEY_AGREEMENT_RESPONDER_HPP
#include <cat/crypt/tunnel/KeyAgreement.hpp>
#include <cat/crypt/tunnel/AuthenticatedEncryption.hpp>
#include <cat/threads/Atomic.hpp>
#if defined(CAT_NO_ATOMIC_ADD) || defined(CAT_NO_ATOMIC_SET)
# include <cat/threads/Mutex.hpp>
# define CAT_NO_ATOMIC_RESPONDER
#endif
namespace cat {
class CAT_EXPORT KeyAgreementResponder : public KeyAgreementCommon
{
Leg *b; // Responder's private key (kept secret)
Leg *B; // Responder's public key (pre-shared with initiator)
Leg *B_neutral; // Endian-neutral B
Leg *G_MultPrecomp; // 8-bit table for multiplication
Leg *y[2]; // Responder's ephemeral private key (kept secret)
Leg *Y_neutral[2]; // Responder's ephemeral public key (shared online with initiator)
#if defined(CAT_NO_ATOMIC_RESPONDER)
Mutex m_thread_id_mutex;
#endif // CAT_NO_ATOMIC_RESPONDER
volatile u32 ChallengeCount;
volatile u32 ActiveY;
void Rekey(BigTwistedEdwards *math, FortunaOutput *csprng);
bool AllocateMemory();
void FreeMemory();
public:
KeyAgreementResponder();
~KeyAgreementResponder();
bool Initialize(BigTwistedEdwards *math, FortunaOutput *csprng,
const u8 *responder_public_key, int public_bytes,
const u8 *responder_private_key, int private_bytes);
public:
bool ProcessChallenge(BigTwistedEdwards *math, FortunaOutput *csprng,
const u8 *initiator_challenge, int challenge_bytes,
u8 *responder_answer, int answer_bytes, Skein *key_hash);
inline bool KeyEncryption(Skein *key_hash, AuthenticatedEncryption *auth_enc, const char *key_name)
{
return auth_enc->SetKey(KeyBytes, key_hash, false, key_name);
}
// Public key is filled if proof succeeds, and will return true
bool VerifyInitiatorIdentity(BigTwistedEdwards *math,
const u8 *responder_answer, int answer_bytes,
const u8 *proof, int proof_bytes,
u8 *public_key, int public_bytes);
public:
bool Sign(BigTwistedEdwards *math, FortunaOutput *csprng,
const u8 *message, int message_bytes,
u8 *signature, int signature_bytes);
};
} // namespace cat
#endif // CAT_KEY_AGREEMENT_RESPONDER_HPP

View File

@ -0,0 +1,47 @@
/*
Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of LibCat nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CAT_KEY_MAKER_HPP
#define CAT_KEY_MAKER_HPP
#include <cat/crypt/tunnel/KeyAgreement.hpp>
#include <cat/crypt/rand/Fortuna.hpp>
namespace cat {
class KeyMaker : public KeyAgreementCommon
{
public:
bool GenerateKeyPair(BigTwistedEdwards *math, FortunaOutput *csprng, u8 *public_key, int public_bytes, u8 *private_key, int private_bytes);
};
} // namespace cat
#endif // CAT_KEY_MAKER_HPP