1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-22 22:34:33 +01:00

Initial version of key-based auth

This commit is contained in:
Michał Janiszewski
2016-05-19 10:23:42 +02:00
parent 1c459c8186
commit 715ba561b1
4 changed files with 354 additions and 78 deletions

View File

@@ -20,6 +20,7 @@
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <vector>
#define KEY_LENGTH_BITS 2048
#define KEY_TYPE EVP_PKEY_RSA
@@ -37,6 +38,7 @@ NetworkKey::~NetworkKey()
Unload();
if (m_ctx != nullptr) {
EVP_PKEY_CTX_free(m_ctx);
m_ctx = nullptr;
}
}
@@ -57,19 +59,21 @@ bool NetworkKey::Generate()
#if KEY_TYPE == EVP_PKEY_RSA
if (!EVP_PKEY_CTX_set_rsa_keygen_bits(m_ctx, KEY_LENGTH_BITS)) {
log_error("Failed to set keygen params");
EVP_PKEY_CTX_free(m_ctx);
m_ctx = nullptr;
return false;
}
#else
#error Only RSA is supported!
#endif
if (!EVP_PKEY_keygen(m_ctx, &m_key)) {
log_error("Failed to generate new key!");
EVP_PKEY_CTX_free(m_ctx);
m_ctx = nullptr;
if (EVP_PKEY_keygen_init(m_ctx) <= 0) {
log_error("Failed to initialise keygen algorithm");
return false;
}
if (EVP_PKEY_keygen(m_ctx, &m_key) <= 0) {
log_error("Failed to generate new key!");
return false;
} else {
log_warning("key ok");
}
log_verbose("New key of type %d, length %d generated successfully.", KEY_TYPE, KEY_LENGTH_BITS);
return true;
}
@@ -84,19 +88,20 @@ bool NetworkKey::LoadPrivate(SDL_RWops *file)
log_error("Key file suspiciously large, refusing to load it");
return false;
}
BIO *bio = BIO_new(BIO_s_mem());
if (bio == nullptr) {
log_error("Failed to initialise OpenSSL's BIO!");
return false;
}
char *priv_key = new char[size];
file->read(file, priv_key, 1, size);
BIO_write(bio, priv_key, size);
BIO *bio = BIO_new_mem_buf(priv_key, size);
if (bio == nullptr) {
log_error("Failed to initialise OpenSSL's BIO!");
delete [] priv_key;
return false;
}
RSA *rsa;
PEM_read_bio_RSAPrivateKey(bio, &rsa, nullptr, nullptr);
rsa = PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, nullptr);
if (!RSA_check_key(rsa)) {
log_error("Loaded RSA key is invalid");
BIO_free_all(bio);
delete [] priv_key;
return false;
}
if (m_key != nullptr) {
@@ -106,6 +111,7 @@ bool NetworkKey::LoadPrivate(SDL_RWops *file)
EVP_PKEY_set1_RSA(m_key, rsa);
BIO_free_all(bio);
RSA_free(rsa);
delete [] priv_key;
return true;
}
@@ -119,16 +125,16 @@ bool NetworkKey::LoadPublic(SDL_RWops *file)
log_error("Key file suspiciously large, refusing to load it");
return false;
}
BIO *bio = BIO_new(BIO_s_mem());
char *pub_key = new char[size];
file->read(file, pub_key, 1, size);
BIO *bio = BIO_new_mem_buf(pub_key, size);
if (bio == nullptr) {
log_error("Failed to initialise OpenSSL's BIO!");
delete [] pub_key;
return false;
}
char *priv_key = new char[size];
file->read(file, priv_key, 1, size);
BIO_write(bio, priv_key, size);
RSA *rsa;
PEM_read_bio_RSAPublicKey(bio, &rsa, nullptr, nullptr);
rsa = PEM_read_bio_RSAPublicKey(bio, nullptr, nullptr, nullptr);
if (m_key != nullptr) {
EVP_PKEY_free(m_key);
}
@@ -136,6 +142,7 @@ bool NetworkKey::LoadPublic(SDL_RWops *file)
EVP_PKEY_set1_RSA(m_key, rsa);
BIO_free_all(bio);
RSA_free(rsa);
delete [] pub_key;
return true;
}
@@ -172,6 +179,7 @@ bool NetworkKey::SavePrivate(SDL_RWops *file)
char *pem_key = new char[keylen];
BIO_read(bio, pem_key, keylen);
file->write(file, pem_key, keylen, 1);
log_verbose("saving key of length %u", keylen);
BIO_free_all(bio);
delete [] pem_key;
#else
@@ -215,7 +223,7 @@ bool NetworkKey::SavePublic(SDL_RWops *file)
return true;
}
char *NetworkKey::PublicKeyString()
std::string NetworkKey::PublicKeyString()
{
if (m_key == nullptr) {
log_error("No key loaded");
@@ -246,11 +254,56 @@ char *NetworkKey::PublicKeyString()
BIO_read(bio, pem_key, keylen);
BIO_free_all(bio);
pem_key[keylen] = '\0';
std::string pem_key_out(pem_key);
free(pem_key);
return pem_key;
return pem_key_out;
}
bool NetworkKey::Sign(const char *md, const size_t len, char **signature, size_t *out_size)
/**
* @brief NetworkKey::PublicKeyHash
* Computes a short, human-readable (e.g. asciif-ied hex) hash for a given
* public key. Serves a purpose of easy identification keys in multiplayer
* overview, multiplayer settings.
*
* In particular, any of digest functions applied to a standarised key
* representation, like PEM, will be sufficient.
*
* @return returns a string containing key hash.
*/
std::string NetworkKey::PublicKeyHash()
{
std::string key = PublicKeyString();
if (key.empty()) {
log_error("No key found");
return nullptr;
}
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
if (EVP_DigestInit_ex(ctx, EVP_sha1(), nullptr) <= 0) {
log_error("Failed to initialise digest context");
EVP_MD_CTX_destroy(ctx);
return nullptr;
}
if (EVP_DigestUpdate(ctx, key.c_str(), key.size()) <= 0) {
log_error("Failed to update digset");
EVP_MD_CTX_destroy(ctx);
return nullptr;
}
unsigned int digest_size = EVP_MAX_MD_SIZE;
std::vector<unsigned char> digest(EVP_MAX_MD_SIZE);
// Cleans up `ctx` automatically.
EVP_DigestFinal(ctx, digest.data(), &digest_size);
std::string digest_out;
digest_out.reserve(EVP_MAX_MD_SIZE * 2 + 1);
for (int i = 0; i < digest_size; i++) {
char buf[3];
sprintf(buf, "%02x", digest[i]);
digest_out.append(buf);
}
return digest_out;
}
bool NetworkKey::Sign(const char *md, const size_t len, char **signature, unsigned int *out_size)
{
EVP_MD_CTX *mdctx = NULL;
@@ -335,6 +388,6 @@ bool NetworkKey::Verify(const char *md, const size_t len, const char* sig, const
} else {
EVP_MD_CTX_destroy(mdctx);
log_error("Signature is invalid");
return true;
return false;
}
}