XChaCha20-Poly1305 via libsodium
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
add_library(EterBase STATIC ${FILE_SOURCES})
|
||||
|
||||
target_link_libraries(EterBase
|
||||
target_link_libraries(EterBase
|
||||
lzo2
|
||||
cryptopp-static
|
||||
sodium
|
||||
mio
|
||||
)
|
||||
|
||||
|
||||
294
src/EterBase/SecureCipher.cpp
Normal file
294
src/EterBase/SecureCipher.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include "StdAfx.h"
|
||||
#include "SecureCipher.h"
|
||||
|
||||
// Static initialization flag for libsodium
|
||||
static bool s_sodiumInitialized = false;
|
||||
|
||||
static bool EnsureSodiumInit()
|
||||
{
|
||||
if (!s_sodiumInitialized)
|
||||
{
|
||||
if (sodium_init() < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
s_sodiumInitialized = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SecureCipher::SecureCipher()
|
||||
{
|
||||
sodium_memzero(m_pk, sizeof(m_pk));
|
||||
sodium_memzero(m_sk, sizeof(m_sk));
|
||||
sodium_memzero(m_tx_key, sizeof(m_tx_key));
|
||||
sodium_memzero(m_rx_key, sizeof(m_rx_key));
|
||||
sodium_memzero(m_session_token, sizeof(m_session_token));
|
||||
}
|
||||
|
||||
SecureCipher::~SecureCipher()
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
bool SecureCipher::Initialize()
|
||||
{
|
||||
if (!EnsureSodiumInit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate X25519 keypair
|
||||
if (crypto_kx_keypair(m_pk, m_sk) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tx_nonce = 0;
|
||||
m_rx_nonce = 0;
|
||||
m_initialized = true;
|
||||
m_activated = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SecureCipher::CleanUp()
|
||||
{
|
||||
// Securely erase all sensitive key material
|
||||
sodium_memzero(m_sk, sizeof(m_sk));
|
||||
sodium_memzero(m_tx_key, sizeof(m_tx_key));
|
||||
sodium_memzero(m_rx_key, sizeof(m_rx_key));
|
||||
sodium_memzero(m_session_token, sizeof(m_session_token));
|
||||
|
||||
m_initialized = false;
|
||||
m_activated = false;
|
||||
m_tx_nonce = 0;
|
||||
m_rx_nonce = 0;
|
||||
}
|
||||
|
||||
void SecureCipher::GetPublicKey(uint8_t* out_pk) const
|
||||
{
|
||||
memcpy(out_pk, m_pk, PK_SIZE);
|
||||
}
|
||||
|
||||
bool SecureCipher::ComputeClientKeys(const uint8_t* server_pk)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Client: tx_key is for sending TO server, rx_key is for receiving FROM server
|
||||
if (crypto_kx_client_session_keys(m_rx_key, m_tx_key, m_pk, m_sk, server_pk) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecureCipher::ComputeServerKeys(const uint8_t* client_pk)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Server: tx_key is for sending TO client, rx_key is for receiving FROM client
|
||||
if (crypto_kx_server_session_keys(m_rx_key, m_tx_key, m_pk, m_sk, client_pk) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SecureCipher::GenerateChallenge(uint8_t* out_challenge)
|
||||
{
|
||||
randombytes_buf(out_challenge, CHALLENGE_SIZE);
|
||||
}
|
||||
|
||||
void SecureCipher::ComputeChallengeResponse(const uint8_t* challenge, uint8_t* out_response)
|
||||
{
|
||||
// HMAC the challenge using our rx_key as the authentication key
|
||||
// This proves we derived the correct shared secret
|
||||
crypto_auth(out_response, challenge, CHALLENGE_SIZE, m_rx_key);
|
||||
}
|
||||
|
||||
bool SecureCipher::VerifyChallengeResponse(const uint8_t* challenge, const uint8_t* response)
|
||||
{
|
||||
// Verify the HMAC - peer should have used their tx_key (our rx_key) to compute it
|
||||
return crypto_auth_verify(response, challenge, CHALLENGE_SIZE, m_rx_key) == 0;
|
||||
}
|
||||
|
||||
void SecureCipher::BuildNonce(uint8_t* nonce, uint64_t counter, bool is_tx)
|
||||
{
|
||||
// 24-byte nonce structure:
|
||||
// [0]: direction flag (0x01 for tx, 0x02 for rx)
|
||||
// [1-7]: reserved/zero
|
||||
// [8-15]: 64-bit counter (little-endian)
|
||||
// [16-23]: reserved/zero
|
||||
|
||||
sodium_memzero(nonce, NONCE_SIZE);
|
||||
nonce[0] = is_tx ? 0x01 : 0x02;
|
||||
|
||||
// Store counter in little-endian at offset 8
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
nonce[8 + i] = (uint8_t)(counter >> (i * 8));
|
||||
}
|
||||
}
|
||||
|
||||
size_t SecureCipher::Encrypt(const void* plaintext, size_t plaintext_len, void* ciphertext)
|
||||
{
|
||||
if (!m_activated)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t nonce[NONCE_SIZE];
|
||||
BuildNonce(nonce, m_tx_nonce, true);
|
||||
|
||||
unsigned long long ciphertext_len = 0;
|
||||
|
||||
if (crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
(uint8_t*)ciphertext, &ciphertext_len,
|
||||
(const uint8_t*)plaintext, plaintext_len,
|
||||
nullptr, 0, // No additional data
|
||||
nullptr, // No secret nonce
|
||||
nonce,
|
||||
m_tx_key) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
++m_tx_nonce;
|
||||
return (size_t)ciphertext_len;
|
||||
}
|
||||
|
||||
size_t SecureCipher::Decrypt(const void* ciphertext, size_t ciphertext_len, void* plaintext)
|
||||
{
|
||||
if (!m_activated)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ciphertext_len < TAG_SIZE)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t nonce[NONCE_SIZE];
|
||||
BuildNonce(nonce, m_rx_nonce, false);
|
||||
|
||||
unsigned long long plaintext_len = 0;
|
||||
|
||||
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
(uint8_t*)plaintext, &plaintext_len,
|
||||
nullptr, // No secret nonce output
|
||||
(const uint8_t*)ciphertext, ciphertext_len,
|
||||
nullptr, 0, // No additional data
|
||||
nonce,
|
||||
m_rx_key) != 0)
|
||||
{
|
||||
// Decryption failed - either wrong key, tampered data, or replay attack
|
||||
return 0;
|
||||
}
|
||||
|
||||
++m_rx_nonce;
|
||||
return (size_t)plaintext_len;
|
||||
}
|
||||
|
||||
void SecureCipher::EncryptInPlace(void* buffer, size_t len)
|
||||
{
|
||||
if (!m_activated || len == 0)
|
||||
return;
|
||||
|
||||
uint8_t nonce[NONCE_SIZE];
|
||||
BuildNonce(nonce, m_tx_nonce, true);
|
||||
|
||||
crypto_stream_xchacha20_xor_ic(
|
||||
(uint8_t*)buffer,
|
||||
(const uint8_t*)buffer,
|
||||
(unsigned long long)len,
|
||||
nonce,
|
||||
0,
|
||||
m_tx_key);
|
||||
|
||||
++m_tx_nonce;
|
||||
}
|
||||
|
||||
void SecureCipher::DecryptInPlace(void* buffer, size_t len)
|
||||
{
|
||||
if (!m_activated || len == 0)
|
||||
return;
|
||||
|
||||
uint8_t nonce[NONCE_SIZE];
|
||||
BuildNonce(nonce, m_rx_nonce, false);
|
||||
|
||||
crypto_stream_xchacha20_xor_ic(
|
||||
(uint8_t*)buffer,
|
||||
(const uint8_t*)buffer,
|
||||
(unsigned long long)len,
|
||||
nonce,
|
||||
0,
|
||||
m_rx_key);
|
||||
|
||||
++m_rx_nonce;
|
||||
}
|
||||
|
||||
bool SecureCipher::EncryptToken(const uint8_t* plaintext, size_t len,
|
||||
uint8_t* ciphertext, uint8_t* nonce_out)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate random nonce for this one-time encryption
|
||||
randombytes_buf(nonce_out, NONCE_SIZE);
|
||||
|
||||
unsigned long long ciphertext_len = 0;
|
||||
|
||||
if (crypto_aead_xchacha20poly1305_ietf_encrypt(
|
||||
ciphertext, &ciphertext_len,
|
||||
plaintext, len,
|
||||
nullptr, 0,
|
||||
nullptr,
|
||||
nonce_out,
|
||||
m_tx_key) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SecureCipher::DecryptToken(const uint8_t* ciphertext, size_t len,
|
||||
const uint8_t* nonce, uint8_t* plaintext)
|
||||
{
|
||||
if (!m_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long long plaintext_len = 0;
|
||||
|
||||
if (crypto_aead_xchacha20poly1305_ietf_decrypt(
|
||||
plaintext, &plaintext_len,
|
||||
nullptr,
|
||||
ciphertext, len,
|
||||
nullptr, 0,
|
||||
nonce,
|
||||
m_rx_key) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SecureCipher::SetSessionToken(const uint8_t* token)
|
||||
{
|
||||
memcpy(m_session_token, token, SESSION_TOKEN_SIZE);
|
||||
}
|
||||
102
src/EterBase/SecureCipher.h
Normal file
102
src/EterBase/SecureCipher.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <sodium.h>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
class SecureCipher {
|
||||
public:
|
||||
// libsodium constants
|
||||
static constexpr size_t PK_SIZE = crypto_kx_PUBLICKEYBYTES; // 32
|
||||
static constexpr size_t SK_SIZE = crypto_kx_SECRETKEYBYTES; // 32
|
||||
static constexpr size_t KEY_SIZE = crypto_kx_SESSIONKEYBYTES; // 32
|
||||
static constexpr size_t NONCE_SIZE = crypto_aead_xchacha20poly1305_ietf_NPUBBYTES; // 24
|
||||
static constexpr size_t TAG_SIZE = crypto_aead_xchacha20poly1305_ietf_ABYTES; // 16
|
||||
static constexpr size_t CHALLENGE_SIZE = 32;
|
||||
static constexpr size_t SESSION_TOKEN_SIZE = 32;
|
||||
static constexpr size_t HMAC_SIZE = crypto_auth_BYTES; // 32
|
||||
|
||||
SecureCipher();
|
||||
~SecureCipher();
|
||||
|
||||
// Initialization - generates keypair
|
||||
bool Initialize();
|
||||
void CleanUp();
|
||||
|
||||
// Key exchange
|
||||
void GetPublicKey(uint8_t* out_pk) const;
|
||||
|
||||
// Client computes session keys from server's public key
|
||||
bool ComputeClientKeys(const uint8_t* server_pk);
|
||||
|
||||
// Server computes session keys from client's public key
|
||||
bool ComputeServerKeys(const uint8_t* client_pk);
|
||||
|
||||
// Challenge-response for authentication
|
||||
void GenerateChallenge(uint8_t* out_challenge);
|
||||
void ComputeChallengeResponse(const uint8_t* challenge, uint8_t* out_response);
|
||||
bool VerifyChallengeResponse(const uint8_t* challenge, const uint8_t* response);
|
||||
|
||||
// AEAD encryption - output is len + TAG_SIZE bytes
|
||||
// Returns actual ciphertext length (plaintext_len + TAG_SIZE)
|
||||
size_t Encrypt(const void* plaintext, size_t plaintext_len, void* ciphertext);
|
||||
|
||||
// AEAD decryption - input must be ciphertext_len bytes (includes TAG_SIZE)
|
||||
// Returns actual plaintext length, or 0 on failure
|
||||
size_t Decrypt(const void* ciphertext, size_t ciphertext_len, void* plaintext);
|
||||
|
||||
// In-place stream encryption for network buffers (XChaCha20, no tag overhead)
|
||||
// Same length in/out. Nonce counter prevents replay.
|
||||
void EncryptInPlace(void* buffer, size_t len);
|
||||
void DecryptInPlace(void* buffer, size_t len);
|
||||
|
||||
// Encrypt a single token with explicit nonce (for KeyComplete packet)
|
||||
bool EncryptToken(const uint8_t* plaintext, size_t len,
|
||||
uint8_t* ciphertext, uint8_t* nonce_out);
|
||||
bool DecryptToken(const uint8_t* ciphertext, size_t len,
|
||||
const uint8_t* nonce, uint8_t* plaintext);
|
||||
|
||||
// State
|
||||
bool IsActivated() const { return m_activated; }
|
||||
void SetActivated(bool value) { m_activated = value; }
|
||||
bool IsInitialized() const { return m_initialized; }
|
||||
|
||||
// Session token management
|
||||
void SetSessionToken(const uint8_t* token);
|
||||
const uint8_t* GetSessionToken() const { return m_session_token; }
|
||||
|
||||
// Get current nonce counters (for debugging/logging)
|
||||
uint64_t GetTxNonce() const { return m_tx_nonce; }
|
||||
uint64_t GetRxNonce() const { return m_rx_nonce; }
|
||||
|
||||
// Access keys directly (for special decrypt operations like session token)
|
||||
const uint8_t* GetRxKey() const { return m_rx_key; }
|
||||
const uint8_t* GetTxKey() const { return m_tx_key; }
|
||||
|
||||
// Alias for convenience
|
||||
void ComputeResponse(const uint8_t* challenge, uint8_t* out_response) {
|
||||
ComputeChallengeResponse(challenge, out_response);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_initialized = false;
|
||||
bool m_activated = false;
|
||||
|
||||
// X25519 keypair
|
||||
uint8_t m_pk[PK_SIZE];
|
||||
uint8_t m_sk[SK_SIZE];
|
||||
|
||||
// Session keys derived from key exchange
|
||||
uint8_t m_tx_key[KEY_SIZE]; // Key for encrypting outgoing packets
|
||||
uint8_t m_rx_key[KEY_SIZE]; // Key for decrypting incoming packets
|
||||
|
||||
// Nonce counters - prevent replay attacks
|
||||
uint64_t m_tx_nonce = 0;
|
||||
uint64_t m_rx_nonce = 0;
|
||||
|
||||
// Server-generated session token
|
||||
uint8_t m_session_token[SESSION_TOKEN_SIZE];
|
||||
|
||||
// Build 24-byte nonce from counter
|
||||
void BuildNonce(uint8_t* nonce, uint64_t counter, bool is_tx);
|
||||
};
|
||||
@@ -1,414 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "cipher.h"
|
||||
|
||||
#ifdef _IMPROVED_PACKET_ENCRYPTION_
|
||||
|
||||
//#pragma warning(push)
|
||||
//#pragma warning(disable: 4100 4127 4189 4231 4512 4706)
|
||||
|
||||
#include <modes.h>
|
||||
#include <nbtheory.h>
|
||||
#include <osrng.h>
|
||||
|
||||
// Diffie-Hellman key agreement
|
||||
#include <dh.h>
|
||||
#include <dh2.h>
|
||||
|
||||
// AES winner and candidates
|
||||
#include <aes.h>
|
||||
#include <cast.h>
|
||||
#include <rc6.h>
|
||||
#include <mars.h>
|
||||
#include <serpent.h>
|
||||
#include <twofish.h>
|
||||
// Other block ciphers
|
||||
#include <blowfish.h>
|
||||
#include <camellia.h>
|
||||
#include <des.h>
|
||||
#include <idea.h>
|
||||
#include <rc5.h>
|
||||
#include <seed.h>
|
||||
#include <shacal2.h>
|
||||
#include <skipjack.h>
|
||||
#include <tea.h>
|
||||
#include "Debug.h"
|
||||
|
||||
|
||||
using namespace CryptoPP;
|
||||
|
||||
// Block cipher algorithm selector abstract base class.
|
||||
struct BlockCipherAlgorithm {
|
||||
enum {
|
||||
kDefault, // to give more chances to default algorithm
|
||||
// AES winner and candidates
|
||||
// kAES, // Rijndael
|
||||
kRC6,
|
||||
kMARS,
|
||||
kTwofish,
|
||||
kSerpent,
|
||||
kCAST256,
|
||||
// Other block ciphers
|
||||
kIDEA,
|
||||
k3DES, // DES-EDE2
|
||||
kCamellia,
|
||||
kSEED,
|
||||
kRC5,
|
||||
kBlowfish,
|
||||
kTEA,
|
||||
//kSKIPJACK,
|
||||
kSHACAL2,
|
||||
// End sentinel
|
||||
kMaxAlgorithms
|
||||
};
|
||||
|
||||
BlockCipherAlgorithm() {}
|
||||
virtual ~BlockCipherAlgorithm() {}
|
||||
|
||||
static BlockCipherAlgorithm* Pick(int hint);
|
||||
|
||||
virtual int GetBlockSize() const = 0;
|
||||
virtual int GetDefaultKeyLength() const = 0;
|
||||
virtual int GetIVLength() const = 0;
|
||||
|
||||
virtual SymmetricCipher* CreateEncoder(const CryptoPP::byte* key, size_t keylen,
|
||||
const CryptoPP::byte* iv) const = 0;
|
||||
virtual SymmetricCipher* CreateDecoder(const CryptoPP::byte* key, size_t keylen,
|
||||
const CryptoPP::byte* iv) const = 0;
|
||||
};
|
||||
|
||||
// Block cipher (with CTR mode) algorithm selector template class.
|
||||
template<class T>
|
||||
struct BlockCipherDetail : public BlockCipherAlgorithm {
|
||||
BlockCipherDetail() {}
|
||||
virtual ~BlockCipherDetail() {}
|
||||
|
||||
virtual int GetBlockSize() const { return T::BLOCKSIZE; }
|
||||
virtual int GetDefaultKeyLength() const { return T::DEFAULT_KEYLENGTH; }
|
||||
virtual int GetIVLength() const { return T::IV_LENGTH; }
|
||||
|
||||
virtual SymmetricCipher* CreateEncoder(const CryptoPP::byte* key, size_t keylen,
|
||||
const CryptoPP::byte* iv) const
|
||||
{
|
||||
return new typename CTR_Mode<T>::Encryption(key, keylen, iv);
|
||||
}
|
||||
virtual SymmetricCipher* CreateDecoder(const CryptoPP::byte* key, size_t keylen,
|
||||
const CryptoPP::byte* iv) const
|
||||
{
|
||||
return new typename CTR_Mode<T>::Decryption(key, keylen, iv);
|
||||
}
|
||||
};
|
||||
|
||||
// Key agreement scheme abstract class.
|
||||
class KeyAgreement {
|
||||
public:
|
||||
KeyAgreement() {}
|
||||
virtual ~KeyAgreement() {}
|
||||
|
||||
virtual size_t Prepare(void* buffer, size_t* length) = 0;
|
||||
virtual bool Agree(size_t agreed_length, const void* buffer, size_t length) = 0;
|
||||
|
||||
const SecByteBlock& shared() const { return shared_; }
|
||||
|
||||
protected:
|
||||
SecByteBlock shared_;
|
||||
};
|
||||
|
||||
// Crypto++ Unified Diffie-Hellman key agreement scheme implementation.
|
||||
class DH2KeyAgreement : public KeyAgreement {
|
||||
public:
|
||||
DH2KeyAgreement();
|
||||
virtual ~DH2KeyAgreement();
|
||||
|
||||
virtual size_t Prepare(void* buffer, size_t* length);
|
||||
virtual bool Agree(size_t agreed_length, const void* buffer, size_t length);
|
||||
|
||||
private:
|
||||
DH dh_;
|
||||
DH2 dh2_;
|
||||
SecByteBlock spriv_key_;
|
||||
SecByteBlock epriv_key_;
|
||||
};
|
||||
|
||||
Cipher::Cipher()
|
||||
: activated_(false), encoder_(NULL), decoder_(NULL), key_agreement_(NULL) {
|
||||
}
|
||||
|
||||
Cipher::~Cipher() {
|
||||
if (activated_) {
|
||||
CleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
void Cipher::CleanUp() {
|
||||
if (encoder_ != NULL) {
|
||||
delete encoder_;
|
||||
encoder_ = NULL;
|
||||
}
|
||||
if (decoder_ != NULL) {
|
||||
delete decoder_;
|
||||
decoder_ = NULL;
|
||||
}
|
||||
if (key_agreement_ != NULL) {
|
||||
delete key_agreement_;
|
||||
key_agreement_ = NULL;
|
||||
}
|
||||
activated_ = false;
|
||||
}
|
||||
|
||||
size_t Cipher::Prepare(void* buffer, size_t* length) {
|
||||
|
||||
assert(key_agreement_ == NULL);
|
||||
key_agreement_ = new DH2KeyAgreement();
|
||||
assert(key_agreement_ != NULL);
|
||||
size_t agreed_length = key_agreement_->Prepare(buffer, length);
|
||||
if (agreed_length == 0) {
|
||||
delete key_agreement_;
|
||||
key_agreement_ = NULL;
|
||||
}
|
||||
|
||||
return agreed_length;
|
||||
}
|
||||
|
||||
bool Cipher::Activate(bool polarity, size_t agreed_length,
|
||||
const void* buffer, size_t length) {
|
||||
|
||||
assert(activated_ == false);
|
||||
assert(key_agreement_ != NULL);
|
||||
bool result = false;
|
||||
if (key_agreement_->Agree(agreed_length, buffer, length)) {
|
||||
result = SetUp(polarity);
|
||||
}
|
||||
delete key_agreement_;
|
||||
key_agreement_ = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Cipher::SetUp(bool polarity) {
|
||||
assert(key_agreement_ != NULL);
|
||||
const SecByteBlock& shared = key_agreement_->shared();
|
||||
|
||||
// Pick a block cipher algorithm
|
||||
|
||||
if (shared.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int hint_0 = shared.BytePtr()[*(shared.BytePtr()) % shared.size()];
|
||||
int hint_1 = shared.BytePtr()[*(shared.BytePtr() + 1) % shared.size()];
|
||||
BlockCipherAlgorithm* detail_0 = BlockCipherAlgorithm::Pick(hint_0);
|
||||
BlockCipherAlgorithm* detail_1 = BlockCipherAlgorithm::Pick(hint_1);
|
||||
assert(detail_0 != NULL);
|
||||
assert(detail_1 != NULL);
|
||||
std::unique_ptr<BlockCipherAlgorithm> algorithm_0(detail_0);
|
||||
std::unique_ptr<BlockCipherAlgorithm> algorithm_1(detail_1);
|
||||
|
||||
const size_t key_length_0 = algorithm_0->GetDefaultKeyLength();
|
||||
const size_t iv_length_0 = algorithm_0->GetBlockSize();
|
||||
|
||||
if (shared.size() < key_length_0 || shared.size() < iv_length_0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t key_length_1 = algorithm_1->GetDefaultKeyLength();
|
||||
const size_t iv_length_1 = algorithm_1->GetBlockSize();
|
||||
|
||||
if (shared.size() < key_length_1 || shared.size() < iv_length_1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pick encryption keys and initial vectors
|
||||
|
||||
SecByteBlock key_0(key_length_0), iv_0(iv_length_0);
|
||||
SecByteBlock key_1(key_length_1), iv_1(iv_length_1);
|
||||
|
||||
size_t offset;
|
||||
|
||||
key_0.Assign(shared, key_length_0);
|
||||
offset = key_length_0;
|
||||
offset = std::min(key_length_0, shared.size() - key_length_1);
|
||||
key_1.Assign(shared.BytePtr() + offset, key_length_1);
|
||||
|
||||
offset = shared.size() - iv_length_0;
|
||||
iv_0.Assign(shared.BytePtr() + offset, iv_length_0);
|
||||
offset = (offset < iv_length_1 ? 0 : offset - iv_length_1);
|
||||
iv_1.Assign(shared.BytePtr() + offset, iv_length_1);
|
||||
|
||||
// Create encryption/decryption objects
|
||||
|
||||
if (polarity) {
|
||||
encoder_ = algorithm_1->CreateEncoder(key_1, key_1.size(), iv_1);
|
||||
decoder_ = algorithm_0->CreateDecoder(key_0, key_0.size(), iv_0);
|
||||
} else {
|
||||
encoder_ = algorithm_0->CreateEncoder(key_0, key_0.size(), iv_0);
|
||||
decoder_ = algorithm_1->CreateDecoder(key_1, key_1.size(), iv_1);
|
||||
}
|
||||
|
||||
assert(encoder_ != NULL);
|
||||
assert(decoder_ != NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BlockCipherAlgorithm* BlockCipherAlgorithm::Pick(int hint) {
|
||||
BlockCipherAlgorithm* detail;
|
||||
int selector = hint % kMaxAlgorithms;
|
||||
switch (selector) {
|
||||
//case kAES:
|
||||
// detail = new BlockCipherDetail<AES>();
|
||||
break;
|
||||
case kRC6:
|
||||
detail = new BlockCipherDetail<RC6>();
|
||||
break;
|
||||
case kMARS:
|
||||
detail = new BlockCipherDetail<MARS>();
|
||||
break;
|
||||
case kTwofish:
|
||||
detail = new BlockCipherDetail<Twofish>();
|
||||
break;
|
||||
case kSerpent:
|
||||
detail = new BlockCipherDetail<Serpent>();
|
||||
break;
|
||||
case kCAST256:
|
||||
detail = new BlockCipherDetail<CAST256>();
|
||||
break;
|
||||
case kIDEA:
|
||||
detail = new BlockCipherDetail<IDEA>();
|
||||
break;
|
||||
case k3DES:
|
||||
detail = new BlockCipherDetail<DES_EDE2>();
|
||||
break;
|
||||
case kCamellia:
|
||||
detail = new BlockCipherDetail<Camellia>();
|
||||
break;
|
||||
case kSEED:
|
||||
detail = new BlockCipherDetail<SEED>();
|
||||
break;
|
||||
case kRC5:
|
||||
detail = new BlockCipherDetail<RC5>();
|
||||
break;
|
||||
case kBlowfish:
|
||||
detail = new BlockCipherDetail<Blowfish>();
|
||||
break;
|
||||
case kTEA:
|
||||
detail = new BlockCipherDetail<TEA>();
|
||||
break;
|
||||
// case kSKIPJACK:
|
||||
// detail = new BlockCipherDetail<SKIPJACK>();
|
||||
// break;
|
||||
case kSHACAL2:
|
||||
detail = new BlockCipherDetail<SHACAL2>();
|
||||
break;
|
||||
case kDefault:
|
||||
default:
|
||||
detail = new BlockCipherDetail<Twofish>(); // default algorithm
|
||||
break;
|
||||
}
|
||||
|
||||
return detail;
|
||||
}
|
||||
|
||||
DH2KeyAgreement::DH2KeyAgreement() : dh_(), dh2_(dh_) {
|
||||
}
|
||||
|
||||
DH2KeyAgreement::~DH2KeyAgreement() {
|
||||
}
|
||||
|
||||
size_t DH2KeyAgreement::Prepare(void* buffer, size_t* length) {
|
||||
// RFC 5114, 1024-bit MODP Group with 160-bit Prime Order Subgroup
|
||||
// http://tools.ietf.org/html/rfc5114#section-2.1
|
||||
Integer p("0xB10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
|
||||
"9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0"
|
||||
"13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
|
||||
"98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0"
|
||||
"A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
|
||||
"DF1FB2BC2E4A4371");
|
||||
|
||||
Integer g("0xA4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
|
||||
"D6406CFF14266D31266FEA1E5C41564B777E690F5504F213"
|
||||
"160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
|
||||
"909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A"
|
||||
"D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
|
||||
"855E6EEB22B3B2E5");
|
||||
|
||||
Integer q("0xF518AA8781A8DF278ABA4E7D64B7CB9D49462353");
|
||||
|
||||
// Schnorr Group primes are of the form p = rq + 1, p and q prime. They
|
||||
// provide a subgroup order. In the case of 1024-bit MODP Group, the
|
||||
// security level is 80 bits (based on the 160-bit prime order subgroup).
|
||||
|
||||
// For a compare/contrast of using the maximum security level, see
|
||||
// dh-unified.zip. Also see http://www.cryptopp.com/wiki/Diffie-Hellman
|
||||
// and http://www.cryptopp.com/wiki/Security_level .
|
||||
|
||||
AutoSeededRandomPool rnd;
|
||||
|
||||
dh_.AccessGroupParameters().Initialize(p, q, g);
|
||||
|
||||
if(!dh_.GetGroupParameters().ValidateGroup(rnd, 3)) {
|
||||
// Failed to validate prime and generator
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
|
||||
p = dh_.GetGroupParameters().GetModulus();
|
||||
q = dh_.GetGroupParameters().GetSubgroupOrder();
|
||||
g = dh_.GetGroupParameters().GetGenerator();
|
||||
|
||||
// http://groups.google.com/group/sci.crypt/browse_thread/thread/7dc7eeb04a09f0ce
|
||||
Integer v = ModularExponentiation(g, q, p);
|
||||
|
||||
if(v != Integer::One()) {
|
||||
// Failed to verify order of the subgroup
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
spriv_key_.New(dh2_.StaticPrivateKeyLength());
|
||||
epriv_key_.New(dh2_.EphemeralPrivateKeyLength());
|
||||
SecByteBlock spub_key(dh2_.StaticPublicKeyLength());
|
||||
SecByteBlock epub_key(dh2_.EphemeralPublicKeyLength());
|
||||
|
||||
dh2_.GenerateStaticKeyPair(rnd, spriv_key_, spub_key);
|
||||
dh2_.GenerateEphemeralKeyPair(rnd, epriv_key_, epub_key);
|
||||
|
||||
// Prepare key agreement data
|
||||
const size_t spub_key_length = spub_key.size();
|
||||
const size_t epub_key_length = epub_key.size();
|
||||
const size_t data_length = spub_key_length + epub_key_length;
|
||||
|
||||
if (*length < data_length) {
|
||||
// Not enough data buffer length b-l-a-c-k
|
||||
return 0;
|
||||
}
|
||||
|
||||
*length = data_length;
|
||||
CryptoPP::byte* buf = (CryptoPP::byte*)buffer;
|
||||
memcpy(buf, spub_key.BytePtr(), spub_key_length);
|
||||
memcpy(buf + spub_key_length, epub_key.BytePtr(), epub_key_length);
|
||||
|
||||
return dh2_.AgreedValueLength();
|
||||
}
|
||||
|
||||
bool DH2KeyAgreement::Agree(size_t agreed_length, const void* buffer, size_t length) {
|
||||
if (agreed_length != dh2_.AgreedValueLength()) {
|
||||
// Shared secret size mismatch
|
||||
return false;
|
||||
}
|
||||
const size_t spub_key_length = dh2_.StaticPublicKeyLength();
|
||||
if (const size_t epub_key_length = dh2_.EphemeralPublicKeyLength(); length != (spub_key_length + epub_key_length)) {
|
||||
// Wrong data length
|
||||
return false;
|
||||
}
|
||||
shared_.New(dh2_.AgreedValueLength());
|
||||
if (const CryptoPP::byte* buf = (const CryptoPP::byte*)buffer; !dh2_.Agree(shared_, spriv_key_, epriv_key_, buf, buf + spub_key_length)) {
|
||||
// Failed to reach shared secret
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef __CIPHER_H__
|
||||
#define __CIPHER_H__
|
||||
|
||||
#ifdef _IMPROVED_PACKET_ENCRYPTION_
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100 4127 4189 4231 4512 4706)
|
||||
#include <cryptlib.h>
|
||||
#pragma warning(pop)
|
||||
// Forward declaration
|
||||
class KeyAgreement;
|
||||
|
||||
// Communication channel encryption handler.
|
||||
class Cipher {
|
||||
public:
|
||||
explicit Cipher();
|
||||
~Cipher();
|
||||
|
||||
void CleanUp();
|
||||
|
||||
// Returns agreed value length in CryptoPP::bytes, or zero on failure.
|
||||
size_t Prepare(void* buffer, size_t* length);
|
||||
// Try to activate cipher algorithm with agreement data received from peer.
|
||||
bool Activate(bool polarity, size_t agreed_length,
|
||||
const void* buffer, size_t length);
|
||||
|
||||
// Encrypts the given block of data. (no padding required)
|
||||
void Encrypt(void* buffer, size_t length) {
|
||||
assert(activated_);
|
||||
if (!activated_) {
|
||||
return;
|
||||
}
|
||||
encoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length);
|
||||
}
|
||||
// Decrypts the given block of data. (no padding required)
|
||||
void Decrypt(void* buffer, size_t length) {
|
||||
assert(activated_);
|
||||
if (!activated_) {
|
||||
return;
|
||||
}
|
||||
decoder_->ProcessData((CryptoPP::byte*)buffer, (const CryptoPP::byte*)buffer, length);
|
||||
}
|
||||
|
||||
bool activated() const { return activated_; }
|
||||
|
||||
void set_activated(bool value) { activated_ = value; }
|
||||
|
||||
private:
|
||||
bool SetUp(bool polarity);
|
||||
|
||||
bool activated_;
|
||||
|
||||
CryptoPP::SymmetricCipher* encoder_;
|
||||
CryptoPP::SymmetricCipher* decoder_;
|
||||
|
||||
KeyAgreement* key_agreement_;
|
||||
};
|
||||
|
||||
#endif // _IMPROVED_PACKET_ENCRYPTION_
|
||||
|
||||
#endif // __CIPHER_H__
|
||||
@@ -1,101 +1,87 @@
|
||||
/*
|
||||
* Filename: tea.c
|
||||
* Description: TEA 암호화 모듈
|
||||
*
|
||||
* Author: 김한주 (aka. 비엽, Cronan), 송영진 (aka. myevan, 빗자루)
|
||||
*/
|
||||
#include "StdAfx.h"
|
||||
* Symmetric encryption module using libsodium (XChaCha20)
|
||||
* API-compatible replacement for legacy TEA encryption
|
||||
*
|
||||
* Key expansion: 16-byte input key is hashed to 32 bytes using BLAKE2b
|
||||
* Nonce: Derived deterministically from the key to ensure encrypt/decrypt consistency
|
||||
*/
|
||||
#include "tea.h"
|
||||
#include <memory.h>
|
||||
#include <sodium.h>
|
||||
#include <cstring>
|
||||
|
||||
/*
|
||||
* TEA Encryption Module Instruction
|
||||
* Edited by 김한주 aka. 비엽, Cronan
|
||||
*
|
||||
* void tea_code(const unsigned long sz, const unsigned long sy, const unsigned long *key, unsigned long *dest)
|
||||
* void tea_decode(const unsigned long sz, const unsigned long sy, const unsigned long *key, unsigned long *dest)
|
||||
* 8바이트를 암호/복호화 할때 사용된다. key 는 16 바이트여야 한다.
|
||||
* sz, sy 는 8바이트의 역순으로 대입한다.
|
||||
*
|
||||
* int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
|
||||
* int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
|
||||
* 한꺼번에 8 바이트 이상을 암호/복호화 할때 사용한다. 만약 size 가
|
||||
* 8의 배수가 아니면 8의 배수로 크기를 "늘려서" 암호화 한다.
|
||||
*
|
||||
* ex. tea_code(pdwSrc[1], pdwSrc[0], pdwKey, pdwDest);
|
||||
* tea_decrypt(pdwDest, pdwSrc, pdwKey, nSize);
|
||||
*/
|
||||
// Fixed context string for key derivation
|
||||
static const char* KEY_CONTEXT = "M2DevPackEncrypt";
|
||||
|
||||
#define TEA_ROUND 32 // 32 를 권장하며, 높을 수록 결과가 난해해 진다.
|
||||
#define DELTA 0x9E3779B9 // DELTA 값 바꾸지 말것.
|
||||
|
||||
void tea_code(const unsigned long sz, const unsigned long sy, const unsigned long *key, unsigned long *dest)
|
||||
// Derive a 32-byte key and 24-byte nonce from the 16-byte input key
|
||||
static void DeriveKeyAndNonce(const unsigned long* key, uint8_t* derived_key, uint8_t* nonce)
|
||||
{
|
||||
unsigned long y = sy, z = sz, sum = 0;
|
||||
unsigned long n = TEA_ROUND;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
y += ((z << 4 ^ z >> 5) + z) ^ (sum + key[sum & 3]);
|
||||
sum += DELTA;
|
||||
z += ((y << 4 ^ y >> 5) + y) ^ (sum + key[sum >> 11 & 3]);
|
||||
}
|
||||
|
||||
*(dest++) = y;
|
||||
*dest = z;
|
||||
// Use BLAKE2b to derive a 32-byte key
|
||||
crypto_generichash(derived_key, crypto_stream_xchacha20_KEYBYTES,
|
||||
(const uint8_t*)key, TEA_KEY_LENGTH,
|
||||
(const uint8_t*)KEY_CONTEXT, strlen(KEY_CONTEXT));
|
||||
|
||||
// Derive nonce from key (using different context)
|
||||
uint8_t nonce_seed[crypto_stream_xchacha20_NONCEBYTES + 8];
|
||||
crypto_generichash(nonce_seed, sizeof(nonce_seed),
|
||||
(const uint8_t*)key, TEA_KEY_LENGTH,
|
||||
(const uint8_t*)"M2DevNonce", 10);
|
||||
memcpy(nonce, nonce_seed, crypto_stream_xchacha20_NONCEBYTES);
|
||||
}
|
||||
|
||||
void tea_decode(const unsigned long sz, const unsigned long sy, const unsigned long *key, unsigned long *dest)
|
||||
int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size)
|
||||
{
|
||||
#pragma warning(disable:4307)
|
||||
unsigned long y = sy, z = sz, sum = DELTA * TEA_ROUND;
|
||||
#pragma warning(default:4307)
|
||||
int resize;
|
||||
|
||||
unsigned long n = TEA_ROUND;
|
||||
|
||||
while (n-- > 0)
|
||||
{
|
||||
z -= ((y << 4 ^ y >> 5) + y) ^ (sum + key[sum >> 11 & 3]);
|
||||
sum -= DELTA;
|
||||
y -= ((z << 4 ^ z >> 5) + z) ^ (sum + key[sum & 3]);
|
||||
}
|
||||
|
||||
*(dest++) = y;
|
||||
*dest = z;
|
||||
}
|
||||
|
||||
int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long * key, int size)
|
||||
{
|
||||
int i;
|
||||
int resize;
|
||||
|
||||
// Align to 8 bytes for compatibility with legacy format
|
||||
if (size % 8 != 0)
|
||||
{
|
||||
resize = size + 8 - (size % 8);
|
||||
memset((char *) src + size, 0, resize - size);
|
||||
// Zero-pad the source (caller must ensure buffer is large enough)
|
||||
memset((char*)src + size, 0, resize - size);
|
||||
}
|
||||
else
|
||||
{
|
||||
resize = size;
|
||||
|
||||
for (i = 0; i < resize >> 3; i++, dest += 2, src += 2)
|
||||
tea_code(*(src + 1), *src, key, dest);
|
||||
|
||||
return (resize);
|
||||
}
|
||||
|
||||
// Derive 32-byte key and nonce from 16-byte input
|
||||
uint8_t derived_key[crypto_stream_xchacha20_KEYBYTES];
|
||||
uint8_t nonce[crypto_stream_xchacha20_NONCEBYTES];
|
||||
DeriveKeyAndNonce(key, derived_key, nonce);
|
||||
|
||||
// XOR encrypt using XChaCha20 stream
|
||||
crypto_stream_xchacha20_xor((uint8_t*)dest, (const uint8_t*)src, resize, nonce, derived_key);
|
||||
|
||||
// Securely clear derived key
|
||||
sodium_memzero(derived_key, sizeof(derived_key));
|
||||
|
||||
return resize;
|
||||
}
|
||||
|
||||
int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long * key, int size)
|
||||
int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size)
|
||||
{
|
||||
int i;
|
||||
int resize;
|
||||
|
||||
if (size % 8 != 0)
|
||||
resize = size + 8 - (size % 8);
|
||||
else
|
||||
resize = size;
|
||||
|
||||
for (i = 0; i < resize >> 3; i++, dest += 2, src += 2)
|
||||
tea_decode(*(src + 1), *src, key, dest);
|
||||
|
||||
return (resize);
|
||||
}
|
||||
int resize;
|
||||
|
||||
// Align to 8 bytes for compatibility with legacy format
|
||||
if (size % 8 != 0)
|
||||
{
|
||||
resize = size + 8 - (size % 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
resize = size;
|
||||
}
|
||||
|
||||
// Derive 32-byte key and nonce from 16-byte input
|
||||
uint8_t derived_key[crypto_stream_xchacha20_KEYBYTES];
|
||||
uint8_t nonce[crypto_stream_xchacha20_NONCEBYTES];
|
||||
DeriveKeyAndNonce(key, derived_key, nonce);
|
||||
|
||||
// XOR decrypt using XChaCha20 stream (same as encrypt for stream cipher)
|
||||
crypto_stream_xchacha20_xor((uint8_t*)dest, (const uint8_t*)src, resize, nonce, derived_key);
|
||||
|
||||
// Securely clear derived key
|
||||
sodium_memzero(derived_key, sizeof(derived_key));
|
||||
|
||||
return resize;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* TEA is a 64-bit symmetric block cipher with a 128-bit key, developed
|
||||
by David J. Wheeler and Roger M. Needham, and described in their
|
||||
paper at <URL:http://www.cl.cam.ac.uk/ftp/users/djw3/tea.ps>.
|
||||
// Symmetric encryption API using libsodium (XChaCha20)
|
||||
// Key is 16 bytes (128-bit), internally expanded to 32 bytes
|
||||
|
||||
This implementation is based on their code in
|
||||
<URL:http://www.cl.cam.ac.uk/ftp/users/djw3/xtea.ps> */
|
||||
#define TEA_KEY_LENGTH 16
|
||||
|
||||
#define TEA_KEY_LENGTH 16
|
||||
|
||||
int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
|
||||
int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
|
||||
int tea_encrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
|
||||
int tea_decrypt(unsigned long *dest, const unsigned long *src, const unsigned long *key, int size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user