v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
437
attic/authentication/StarAuthenticationKey.cpp
Normal file
437
attic/authentication/StarAuthenticationKey.cpp
Normal file
|
@ -0,0 +1,437 @@
|
|||
#include "StarAuthenticationKey.hpp"
|
||||
#include "StarClock.hpp"
|
||||
#include "StarEncode.hpp"
|
||||
#include "StarSha256.hpp"
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/des.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/buffer.h>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <cstring>
|
||||
#include <openssl/x509.h>
|
||||
#include <vector>
|
||||
#include "StarRandom.hpp"
|
||||
|
||||
namespace Star {
|
||||
|
||||
namespace Auth {
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Key &key) {
|
||||
return out << "Private Key :" << key.privateKey() << std::endl << " Public Key :" << key.publicKey() << std::endl;
|
||||
}
|
||||
|
||||
Key::Key() : m_key(NULL), m_mdCtx(NULL) {
|
||||
// ERR_load_crypto_strings will only load things once even if called multiple times
|
||||
ERR_load_crypto_strings();
|
||||
m_key = EVP_PKEY_new();
|
||||
if (m_key == NULL)
|
||||
throw CryptoException("Unable to Establish Key Container");
|
||||
m_mdCtx = EVP_MD_CTX_create();
|
||||
if (m_mdCtx == NULL) {
|
||||
EVP_PKEY_free(m_key);
|
||||
m_key = NULL;
|
||||
throw CryptoException("Unable to Establish EVP Message Digest Context");
|
||||
}
|
||||
}
|
||||
|
||||
Key::Key(bool generate) : Key() {
|
||||
if (generate) {
|
||||
regenerate();
|
||||
}
|
||||
}
|
||||
|
||||
Key::Key(String const& key, bool privateKey) : Key() {
|
||||
if (privateKey) {
|
||||
loadPrivateKey(key);
|
||||
} else {
|
||||
loadPublicKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
Key::~Key() {
|
||||
if (m_key != NULL)
|
||||
EVP_PKEY_free(m_key);
|
||||
if (m_mdCtx != NULL)
|
||||
EVP_MD_CTX_destroy(m_mdCtx);
|
||||
// We could call ERR_free_strings but that would just mean the next Key
|
||||
// to be created would make ERR_load_crypto_strings actually process
|
||||
// so eliminate that thrashing by not calling ERR_free_strings
|
||||
}
|
||||
|
||||
void Key::regenerate() {
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
if (EVP_PKEY_keygen_init(ctx) <= 0)
|
||||
throw cryptoException(ctx, "Unable to initialize EVP Key Generator");
|
||||
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, KeySize) <= 0)
|
||||
throw cryptoException(ctx, "Unable to set EVP Key Generator size");
|
||||
if (EVP_PKEY_keygen(ctx, &m_key) <= 0)
|
||||
throw cryptoException(ctx, "Unable to generate new Client Key");
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
m_isPrivate = 1;
|
||||
}
|
||||
|
||||
CryptoException Key::cryptoException(BIO *bio, const char* errorString) const {
|
||||
unsigned long err = ERR_get_error();
|
||||
BIO_free_all(bio);
|
||||
if (errorString == nullptr) {
|
||||
errorString = ERR_error_string(err, NULL);
|
||||
}
|
||||
return CryptoException(ERR_error_string(err, NULL));
|
||||
}
|
||||
|
||||
CryptoException Key::cryptoException(EVP_PKEY_CTX *ctx, const char* errorString) const {
|
||||
unsigned long err = ERR_get_error();
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
if (errorString == nullptr) {
|
||||
errorString = ERR_error_string(err, NULL);
|
||||
}
|
||||
return CryptoException(errorString);
|
||||
}
|
||||
|
||||
void Key::loadPrivateKey(String const& key) {
|
||||
if (key.empty())
|
||||
throw StarException("Empty key.");
|
||||
BIO *bIn = BIO_push(
|
||||
BIO_new(BIO_f_base64()),
|
||||
BIO_new_mem_buf((void*) key.utf8Ptr(), key.utf8Size())
|
||||
);
|
||||
BIO_set_flags(bIn, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
RSA * rsa;
|
||||
if ((rsa = d2i_RSAPrivateKey_bio(bIn, NULL)) == 0)
|
||||
throw cryptoException(bIn);
|
||||
|
||||
EVP_PKEY_set1_RSA(m_key, rsa);
|
||||
BIO_free_all(bIn);
|
||||
m_isPrivate = 1;
|
||||
}
|
||||
|
||||
void Key::loadPublicKey(String const& key) {
|
||||
if (key.empty())
|
||||
throw StarException("Empty key.");
|
||||
BIO *bIn = BIO_push(
|
||||
BIO_new(BIO_f_base64()),
|
||||
BIO_new_mem_buf((void*) key.utf8Ptr(), key.utf8Size())
|
||||
);
|
||||
BIO_set_flags(bIn, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
RSA * rsa;
|
||||
if ((rsa = d2i_RSAPublicKey_bio(bIn, NULL)) == 0)
|
||||
throw cryptoException(bIn);
|
||||
|
||||
EVP_PKEY_set1_RSA(m_key, rsa);
|
||||
BIO_free_all(bIn);
|
||||
m_isPrivate = 0;
|
||||
}
|
||||
|
||||
String Key::publicKey() const {
|
||||
BIO *b64 = BIO_push(
|
||||
BIO_new(BIO_f_base64()),
|
||||
BIO_new(BIO_s_mem())
|
||||
);
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
i2d_RSAPublicKey_bio(b64, EVP_PKEY_get1_RSA(m_key));
|
||||
(void) BIO_flush(b64);
|
||||
|
||||
BUF_MEM *bPtr;
|
||||
BIO_get_mem_ptr(b64, &bPtr);
|
||||
String out(bPtr->data, bPtr->length);
|
||||
BIO_free_all(b64);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
String Key::privateKey() const {
|
||||
if (!m_isPrivate)
|
||||
throw CryptoException("Private Key not loaded.");
|
||||
BIO *b64 = BIO_push(
|
||||
BIO_new(BIO_f_base64()),
|
||||
BIO_new(BIO_s_mem())
|
||||
);
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
|
||||
i2d_RSAPrivateKey_bio(b64, EVP_PKEY_get1_RSA(m_key));
|
||||
(void) BIO_flush(b64);
|
||||
|
||||
BUF_MEM *bPtr;
|
||||
BIO_get_mem_ptr(b64, &bPtr);
|
||||
String out(bPtr->data, bPtr->length);
|
||||
BIO_free_all(b64);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
String Key::encryptMessage(String const& message) const {
|
||||
size_t bufLen;
|
||||
unsigned char * buf;
|
||||
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(m_key, NULL);
|
||||
if (EVP_PKEY_encrypt_init(ctx) != 1)
|
||||
throw cryptoException(ctx);
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) != 1)
|
||||
throw cryptoException(ctx);
|
||||
|
||||
if (EVP_PKEY_encrypt(ctx, NULL, &bufLen, (unsigned char *) message.utf8Ptr(), message.utf8Size()) != 1)
|
||||
throw cryptoException(ctx);
|
||||
|
||||
buf = new unsigned char[bufLen];
|
||||
if (EVP_PKEY_encrypt(ctx, (unsigned char *) buf, &bufLen, (unsigned char *) message.utf8Ptr(), message.utf8Size()) <= 0) {
|
||||
delete[] buf;
|
||||
throw cryptoException(ctx);
|
||||
}
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
|
||||
String oString(base64Encode((char const*)buf, bufLen));
|
||||
delete[] buf;
|
||||
return oString;
|
||||
}
|
||||
|
||||
String Key::decryptMessage(String const& cryptText) const {
|
||||
ByteArray iString(base64Decode(cryptText));
|
||||
size_t bufLen;
|
||||
unsigned char * buf;
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(m_key, NULL);
|
||||
if (EVP_PKEY_decrypt_init(ctx) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_decrypt(ctx, NULL, &bufLen, (unsigned char *) iString.ptr(), iString.size()) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
buf = new unsigned char[bufLen];
|
||||
if (EVP_PKEY_decrypt(ctx, (unsigned char *) buf, &bufLen, (unsigned char *) iString.ptr(), iString.size()) != 1) {
|
||||
delete[] buf;
|
||||
throw cryptoException(ctx);
|
||||
}
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
String oString((const char *) buf, bufLen);
|
||||
delete[] buf;
|
||||
return oString;
|
||||
}
|
||||
|
||||
String Key::signMessage(String const& message) const {
|
||||
if (!m_isPrivate)
|
||||
throw CryptoException("Private Key not Loaded");
|
||||
size_t sigLen;
|
||||
unsigned char *sig;
|
||||
ByteArray shaMessage(sha256(ByteArray(message.utf8Ptr(), message.utf8Size())));
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(m_key, NULL);
|
||||
if (EVP_PKEY_sign_init(ctx) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_sign(ctx, NULL, &sigLen, (unsigned char *) shaMessage.ptr(), shaMessage.size()) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (!(sig = (unsigned char *) OPENSSL_malloc(sigLen)))
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_sign(ctx, sig, &sigLen, (unsigned char *) shaMessage.ptr(), shaMessage.size()) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
String oString(base64Encode((const char *)sig, sigLen));
|
||||
OPENSSL_free(sig);
|
||||
return oString;
|
||||
}
|
||||
|
||||
bool Key::verifyMessage(String const& message, String const& signature) const {
|
||||
ByteArray shaMessage(sha256(ByteArray(message.utf8Ptr(), message.utf8Size())));
|
||||
ByteArray rawSignature(base64Decode(signature));
|
||||
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(m_key, NULL);
|
||||
if (EVP_PKEY_verify_init(ctx) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PSS_PADDING) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
if (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha256()) <= 0)
|
||||
throw cryptoException(ctx);
|
||||
int rv = EVP_PKEY_verify(ctx, (unsigned char *) rawSignature.ptr(), rawSignature.size(), (unsigned char *) shaMessage.ptr(), shaMessage.size());
|
||||
if (rv < 0)
|
||||
throw cryptoException(ctx);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
return rv == 1;
|
||||
}
|
||||
|
||||
void initializeKeyLogic() {
|
||||
// intitial initialization of DES_random_key and friends scans the whole heap.
|
||||
// do it as early as possible.
|
||||
generateKey();
|
||||
}
|
||||
|
||||
String generateKey() {
|
||||
// Init desKey to week key to avoid valgrind warning
|
||||
DES_cblock desKey = {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01};
|
||||
ByteArray key = ByteArray::withReserve(24);
|
||||
|
||||
if (DES_random_key(&desKey) == 0)
|
||||
throw CryptoException("RNG could not generate a secure key");
|
||||
key.append((const char *) desKey, 8);
|
||||
if (DES_random_key(&desKey) == 0)
|
||||
throw CryptoException("RNG could not generate a secure key");
|
||||
key.append((const char *) desKey, 8);
|
||||
if (DES_random_key(&desKey) == 0)
|
||||
throw CryptoException("RNG could not generate a secure key");
|
||||
key.append((const char *) desKey, 8);
|
||||
return base64Encode(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a Message
|
||||
* @param message Message to Encrypt
|
||||
* @param key Base64 encoded 24 byte key
|
||||
* @return Base64 Encoded String containing encrypted message
|
||||
*/
|
||||
String encryptMessage(String const& message, String const& key) {
|
||||
DES_cblock desKey;
|
||||
DES_cblock ivec;
|
||||
DES_key_schedule desSchedule1;
|
||||
DES_key_schedule desSchedule2;
|
||||
DES_key_schedule desSchedule3;
|
||||
int n = 0;
|
||||
|
||||
/* Prepare the key for use with DES_cfb64_encrypt */
|
||||
ByteArray iKey(base64Decode(key));
|
||||
if (iKey.size() != 24)
|
||||
throw StarException("Key size mismatch.");
|
||||
|
||||
std::memcpy(desKey, iKey.ptr(), 8);
|
||||
DES_set_odd_parity(&desKey);
|
||||
DES_set_key_checked(&desKey, &desSchedule1);
|
||||
std::memcpy(desKey, iKey.ptr() + 8, 8);
|
||||
DES_set_odd_parity(&desKey);
|
||||
DES_set_key_checked(&desKey, &desSchedule2);
|
||||
std::memcpy(desKey, iKey.ptr() + 16, 8);
|
||||
DES_set_odd_parity(&desKey);
|
||||
DES_set_key_checked(&desKey, &desSchedule3);
|
||||
|
||||
std::memcpy(ivec, iKey.ptr() + 12, 8);
|
||||
|
||||
/* Encryption occurs here */
|
||||
unsigned char * buf = new unsigned char[message.utf8Size()];
|
||||
DES_ede3_cfb64_encrypt((unsigned char *) message.utf8Ptr(), buf, message.utf8Size(), &desSchedule1, &desSchedule2, &desSchedule3, &ivec, &n, DES_ENCRYPT);
|
||||
String output(base64Encode((const char*)buf, message.utf8Size()));
|
||||
delete[] buf;
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a Message
|
||||
* @param message Base64 Encoded DES3 encrypted message
|
||||
* @param key Base64 encoded 24 byte key
|
||||
* @return String containing decrypted message
|
||||
*/
|
||||
String decryptMessage(String const& message, String const& key) {
|
||||
DES_cblock desKey;
|
||||
DES_cblock ivec;
|
||||
DES_key_schedule desSchedule1;
|
||||
DES_key_schedule desSchedule2;
|
||||
DES_key_schedule desSchedule3;
|
||||
int n = 0;
|
||||
|
||||
ByteArray iMessage(base64Decode(message));
|
||||
|
||||
/* Prepare the key for use with DES_cfb64_encrypt */
|
||||
ByteArray iKey(base64Decode(key));
|
||||
if (iKey.size() != 24)
|
||||
throw StarException("Key size mismatch.");
|
||||
|
||||
std::memcpy(desKey, iKey.ptr(), 8);
|
||||
DES_set_odd_parity(&desKey);
|
||||
DES_set_key_checked(&desKey, &desSchedule1);
|
||||
std::memcpy(desKey, iKey.ptr() + 8, 8);
|
||||
DES_set_odd_parity(&desKey);
|
||||
DES_set_key_checked(&desKey, &desSchedule2);
|
||||
std::memcpy(desKey, iKey.ptr() + 16, 8);
|
||||
DES_set_odd_parity(&desKey);
|
||||
DES_set_key_checked(&desKey, &desSchedule3);
|
||||
|
||||
std::memcpy(ivec, iKey.ptr() + 12, 8);
|
||||
|
||||
/* Encryption occurs here */
|
||||
unsigned char * buf = new unsigned char[message.utf8Size()];
|
||||
DES_ede3_cfb64_encrypt((unsigned char *) iMessage.ptr(), buf, iMessage.size(), &desSchedule1, &desSchedule2, &desSchedule3, &ivec, &n, DES_DECRYPT);
|
||||
String output((const char *) buf, iMessage.size());
|
||||
delete[] buf;
|
||||
return output;
|
||||
}
|
||||
|
||||
String preHashPassword(String const& username, String const& password) {
|
||||
String passPreHash = username + ":" + password + ":starbound";
|
||||
return base64Encode(sha256(passPreHash.utf8Ptr(), passPreHash.utf8Size()));
|
||||
}
|
||||
|
||||
double const bcrypt_goal_duration = 0.032;
|
||||
double const bcrypt_max_validate_duration = 0.5;
|
||||
double const bcrypt_max_generation_duration = 5.0;
|
||||
int const bcrypt_minimal_rounds = 100;
|
||||
|
||||
String bcrypt(String const& message, String const& salt, int& rounds) {
|
||||
rounds = 0;
|
||||
|
||||
auto startTime = Clock::currentTime();
|
||||
|
||||
ByteArray saltBuffer(salt.utf8Ptr(), salt.utf8Size());
|
||||
ByteArray messageBuffer(sha256(ByteArray(message.utf8Ptr(), message.utf8Size())));
|
||||
ByteArray roundBuffer;
|
||||
|
||||
while ((rounds < bcrypt_minimal_rounds) || (Clock::secondsSince(startTime) < bcrypt_goal_duration)) {
|
||||
roundBuffer.resize(0);
|
||||
roundBuffer.writeFrom(messageBuffer.ptr(), 0, messageBuffer.size());
|
||||
roundBuffer.writeFrom(saltBuffer.ptr(), messageBuffer.size(), saltBuffer.size());
|
||||
sha256(roundBuffer, messageBuffer);
|
||||
rounds++;
|
||||
}
|
||||
return base64Encode(messageBuffer);
|
||||
}
|
||||
|
||||
String bcryptWithRounds(String const& message, String const& salt, int rounds) {
|
||||
if (rounds < bcrypt_minimal_rounds)
|
||||
throw StarException("Not enough rounds for bcrypt.");
|
||||
ByteArray saltBuffer(salt.utf8Ptr(), salt.utf8Size());
|
||||
ByteArray messageBuffer(sha256(ByteArray(message.utf8Ptr(), message.utf8Size())));
|
||||
ByteArray roundBuffer;
|
||||
|
||||
auto startTime = Clock::currentTime();
|
||||
|
||||
while (rounds > 0) {
|
||||
if (Clock::secondsSince(startTime) > bcrypt_max_generation_duration)
|
||||
throw StarException("Timeout generating bcrypt.");
|
||||
roundBuffer.resize(0);
|
||||
roundBuffer.writeFrom(messageBuffer.ptr(), 0, messageBuffer.size());
|
||||
roundBuffer.writeFrom(saltBuffer.ptr(), messageBuffer.size(), saltBuffer.size());
|
||||
sha256(roundBuffer, messageBuffer);
|
||||
rounds--;
|
||||
}
|
||||
return base64Encode(messageBuffer);
|
||||
}
|
||||
|
||||
bool bcryptValidate(String const& message, String const& salt, String const& hash, int rounds) {
|
||||
if (rounds < bcrypt_minimal_rounds)
|
||||
return false;
|
||||
ByteArray saltBuffer(salt.utf8Ptr(), salt.utf8Size());
|
||||
ByteArray messageBuffer(sha256(ByteArray(message.utf8Ptr(), message.utf8Size())));
|
||||
ByteArray roundBuffer;
|
||||
|
||||
auto startTime = Clock::currentTime();
|
||||
|
||||
while (rounds > 0) {
|
||||
if (Clock::secondsSince(startTime) > bcrypt_max_validate_duration)
|
||||
return false;
|
||||
roundBuffer.resize(0);
|
||||
roundBuffer.writeFrom(messageBuffer.ptr(), 0, messageBuffer.size());
|
||||
roundBuffer.writeFrom(saltBuffer.ptr(), messageBuffer.size(), saltBuffer.size());
|
||||
sha256(roundBuffer, messageBuffer);
|
||||
rounds--;
|
||||
}
|
||||
return base64Encode(messageBuffer) == hash;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue