mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
MDEV-32618 new auth plugin
PARSEC: Password Authentication using Response Signed with Elliptic Curve new authentication plugin that uses salted passwords, key derivation, extensible password storage format, and both server- and client-side scrambles. It signs the response with ed25519, but it uses stock unmodified ed25519 as provided by OpenSSL/WolfSSL/GnuTLS. Edited by: Sergei Golubchik
This commit is contained in:
parent
68e369e3a9
commit
e580cf7ae0
14 changed files with 445 additions and 20 deletions
|
@ -7,6 +7,7 @@ ENDIF()
|
|||
SET(CONC_WITH_SIGNCODE ${SIGNCODE})
|
||||
SET(SIGN_OPTIONS ${SIGNTOOL_PARAMETERS})
|
||||
SET(CONC_WITH_EXTERNAL_ZLIB ON)
|
||||
SET(CLIENT_PLUGIN_PARSEC DYNAMIC)
|
||||
|
||||
IF(SSL_DEFINES MATCHES "WOLFSSL")
|
||||
IF(WIN32)
|
||||
|
|
|
@ -54,7 +54,8 @@ MACRO (MYSQL_USE_BUNDLED_SSL)
|
|||
SET(HAVE_EncryptAes128Ctr ON CACHE INTERNAL "wolfssl does support AES-CTR")
|
||||
SET(HAVE_EncryptAes128Gcm OFF CACHE INTERNAL "wolfssl does not support AES-GCM")
|
||||
SET(HAVE_des ON CACHE INTERNAL "wolfssl does support DES API")
|
||||
SET(HAVE_hkdf ON CACHE INTERNAL "wolfssl does support EVP_PKEY API")
|
||||
SET(HAVE_evp_pkey ON CACHE INTERNAL "wolfssl does support EVP_PKEY API")
|
||||
SET(HAVE_hkdf ON CACHE INTERNAL "wolfssl does support EVP_PKEY_HKDF API")
|
||||
CHANGE_SSL_SETTINGS("bundled")
|
||||
ADD_SUBDIRECTORY(extra/wolfssl)
|
||||
MESSAGE_ONCE(SSL_LIBRARIES "SSL_LIBRARIES = ${SSL_LIBRARIES}")
|
||||
|
@ -137,6 +138,8 @@ MACRO (MYSQL_CHECK_SSL)
|
|||
HAVE_EncryptAes128Gcm)
|
||||
CHECK_SYMBOL_EXISTS(DES_set_key_unchecked "openssl/des.h"
|
||||
HAVE_des)
|
||||
CHECK_SYMBOL_EXISTS(EVP_PKEY_get_raw_public_key "openssl/evp.h"
|
||||
HAVE_evp_pkey)
|
||||
CHECK_SYMBOL_EXISTS(EVP_PKEY_CTX_set_hkdf_md "string.h;stdarg.h;openssl/kdf.h"
|
||||
HAVE_hkdf)
|
||||
SET(CMAKE_REQUIRED_INCLUDES)
|
||||
|
|
1
debian/libmariadb3.install
vendored
1
debian/libmariadb3.install
vendored
|
@ -4,3 +4,4 @@ usr/lib/*/libmariadb3/plugin/client_ed25519.so
|
|||
usr/lib/*/libmariadb3/plugin/dialog.so
|
||||
usr/lib/*/libmariadb3/plugin/mysql_clear_password.so
|
||||
usr/lib/*/libmariadb3/plugin/sha256_password.so
|
||||
usr/lib/*/libmariadb3/plugin/parsec.so
|
||||
|
|
1
debian/mariadb-server.install
vendored
1
debian/mariadb-server.install
vendored
|
@ -39,6 +39,7 @@ usr/lib/mysql/plugin/auth_ed25519.so
|
|||
usr/lib/mysql/plugin/auth_pam.so
|
||||
usr/lib/mysql/plugin/auth_pam_tool_dir/auth_pam_tool
|
||||
usr/lib/mysql/plugin/auth_pam_v1.so
|
||||
usr/lib/mysql/plugin/auth_parsec.so
|
||||
usr/lib/mysql/plugin/disks.so
|
||||
usr/lib/mysql/plugin/file_key_management.so
|
||||
usr/lib/mysql/plugin/ha_archive.so
|
||||
|
|
|
@ -60,7 +60,10 @@ ${WOLFCRYPT_SRCDIR}/des3.c
|
|||
${WOLFCRYPT_SRCDIR}/dh.c
|
||||
${WOLFCRYPT_SRCDIR}/dsa.c
|
||||
${WOLFCRYPT_SRCDIR}/ecc.c
|
||||
${WOLFCRYPT_SRCDIR}/ed25519.c
|
||||
${WOLFCRYPT_SRCDIR}/error.c
|
||||
${WOLFCRYPT_SRCDIR}/fe_operations.c
|
||||
${WOLFCRYPT_SRCDIR}/ge_operations.c
|
||||
${WOLFCRYPT_SRCDIR}/hmac.c
|
||||
${WOLFCRYPT_SRCDIR}/logging.c
|
||||
${WOLFCRYPT_SRCDIR}/md4.c
|
||||
|
@ -112,6 +115,7 @@ if(WOLFSSL_INTELASM)
|
|||
${WOLFCRYPT_SRCDIR}/aes_asm.S
|
||||
${WOLFCRYPT_SRCDIR}/aes_gcm_asm.S
|
||||
${WOLFCRYPT_SRCDIR}/chacha_asm.S
|
||||
${WOLFCRYPT_SRCDIR}/fe_x25519_asm.S
|
||||
${WOLFCRYPT_SRCDIR}/poly1305_asm.S
|
||||
${WOLFCRYPT_SRCDIR}/sha512_asm.S
|
||||
${WOLFCRYPT_SRCDIR}/sha256_asm.S
|
||||
|
@ -132,5 +136,7 @@ if(MSVC)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
set_property(TARGET wolfssl PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
CONFIGURE_FILE(user_settings.h.in user_settings.h)
|
||||
|
||||
|
|
|
@ -69,4 +69,6 @@
|
|||
#cmakedefine WOLFSSL_SP_X86_64
|
||||
#cmakedefine WOLFSSL_SP_X86_64_ASM
|
||||
|
||||
#define HAVE_ED25519
|
||||
|
||||
#endif /* WOLFSSL_USER_SETTINGS_H */
|
||||
|
|
32
mysql-test/suite/plugins/r/parsec.result
Normal file
32
mysql-test/suite/plugins/r/parsec.result
Normal file
|
@ -0,0 +1,32 @@
|
|||
install soname 'auth_parsec';
|
||||
create user test1@'%' identified via parsec using 'pwd';
|
||||
ERROR HY000: Operation CREATE USER failed for 'test1'@'%'
|
||||
create user test1@'%' identified via parsec using PASSWORD('pwd');
|
||||
show grants for test1@'%';
|
||||
Grants for test1@%
|
||||
GRANT USAGE ON *.* TO `test1`@`%` IDENTIFIED VIA parsec USING 'P0:salt:password'
|
||||
connect con1, localhost, test1, pwd;
|
||||
select 1, USER(), CURRENT_USER();
|
||||
1 USER() CURRENT_USER()
|
||||
1 test1@localhost test1@%
|
||||
disconnect con1;
|
||||
connect con2, localhost, test1, pwd;
|
||||
select 2, USER(), CURRENT_USER();
|
||||
2 USER() CURRENT_USER()
|
||||
2 test1@localhost test1@%
|
||||
disconnect con2;
|
||||
connect(localhost,test1,wrong_pwd,test,MASTER_MYPORT,MASTER_MYSOCK);
|
||||
connect con3, localhost, test1, wrong_pwd;
|
||||
ERROR 28000: Access denied for user 'test1'@'localhost' (using password: NO)
|
||||
connection default;
|
||||
create function have_ssl() returns char(3)
|
||||
return (select if(variable_value > '','yes','no') as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher');
|
||||
grant execute on test.* to test1@'%';
|
||||
# mysql -utest1 -ppwd --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
test.have_ssl()
|
||||
yes
|
||||
drop function have_ssl;
|
||||
drop user test1@'%';
|
||||
uninstall soname 'auth_parsec';
|
|
@ -1,12 +1,12 @@
|
|||
install soname 'client_ed25519';
|
||||
install soname 'CLIENT_PLUGIN';
|
||||
Got one of the listed errors
|
||||
include/master-slave.inc
|
||||
[connection master]
|
||||
connection slave;
|
||||
install soname 'auth_ed25519';
|
||||
install soname 'auth_PLUGIN';
|
||||
connection master;
|
||||
install soname 'auth_ed25519';
|
||||
create user rpluser@'%' identified via ed25519 using PASSWORD('rpl_pass');
|
||||
install soname 'auth_plugin';
|
||||
create user rpluser@'%' identified via PLUGIN using PASSWORD('rpl_pass');
|
||||
grant replication slave on *.* to rpluser@'%';
|
||||
connection master;
|
||||
connection slave;
|
||||
|
@ -19,7 +19,7 @@ change master to master_user='root', master_password='';
|
|||
include/start_slave.inc
|
||||
include/stop_slave.inc
|
||||
drop user rpluser@'%';
|
||||
uninstall soname 'auth_ed25519';
|
||||
uninstall soname 'auth_plugin';
|
||||
connection master;
|
||||
drop user rpluser@'%';
|
||||
uninstall soname 'auth_ed25519';
|
||||
uninstall soname 'auth_plugin';
|
||||
|
|
3
mysql-test/suite/plugins/t/parsec.opt
Normal file
3
mysql-test/suite/plugins/t/parsec.opt
Normal file
|
@ -0,0 +1,3 @@
|
|||
--ssl-key=
|
||||
--ssl-cert=
|
||||
--ssl-ca=
|
45
mysql-test/suite/plugins/t/parsec.test
Normal file
45
mysql-test/suite/plugins/t/parsec.test
Normal file
|
@ -0,0 +1,45 @@
|
|||
source include/platform.inc;
|
||||
source include/not_embedded.inc;
|
||||
|
||||
if (!$AUTH_PARSEC_SO) {
|
||||
skip No auth_parsec plugin;
|
||||
}
|
||||
if (!$PARSEC_SO) {
|
||||
skip No auth_parsec plugin;
|
||||
}
|
||||
install soname 'auth_parsec';
|
||||
--error ER_CANNOT_USER
|
||||
create user test1@'%' identified via parsec using 'pwd';
|
||||
create user test1@'%' identified via parsec using PASSWORD('pwd');
|
||||
--replace_regex /:[A-Za-z0-9+\/]{43}'/:password'/ /:[A-Za-z0-9+\/]{24}:/:salt:/
|
||||
show grants for test1@'%';
|
||||
connect con1, localhost, test1, pwd;
|
||||
select 1, USER(), CURRENT_USER();
|
||||
disconnect con1;
|
||||
connect con2, localhost, test1, pwd;
|
||||
select 2, USER(), CURRENT_USER();
|
||||
disconnect con2;
|
||||
--replace_result $MASTER_MYSOCK MASTER_MYSOCK $MASTER_MYPORT MASTER_MYPORT
|
||||
--error ER_ACCESS_DENIED_ERROR
|
||||
connect con3, localhost, test1, wrong_pwd;
|
||||
|
||||
connection default;
|
||||
|
||||
create function have_ssl() returns char(3)
|
||||
return (select if(variable_value > '','yes','no') as 'have_ssl'
|
||||
from information_schema.session_status
|
||||
where variable_name='ssl_cipher');
|
||||
grant execute on test.* to test1@'%';
|
||||
|
||||
let host=;
|
||||
if ($MTR_COMBINATION_WIN) {
|
||||
# see ssl_autoverify.test
|
||||
let host=--host=127.0.0.2;
|
||||
}
|
||||
|
||||
--echo # mysql -utest1 -ppwd --ssl-verify-server-cert -e "select test.have_ssl()"
|
||||
--exec $MYSQL --protocol tcp $host -utest1 -ppwd --ssl-verify-server-cert -e "select test.have_ssl()" 2>&1
|
||||
|
||||
drop function have_ssl;
|
||||
drop user test1@'%';
|
||||
uninstall soname 'auth_parsec';
|
2
mysql-test/suite/plugins/t/rpl_auth.combinations
Normal file
2
mysql-test/suite/plugins/t/rpl_auth.combinations
Normal file
|
@ -0,0 +1,2 @@
|
|||
[parsec]
|
||||
[ed25519]
|
|
@ -1,29 +1,43 @@
|
|||
if (!$AUTH_ED25519_SO) {
|
||||
skip No auth_ed25519 plugin;
|
||||
if ($MTR_COMBINATION_ED25519) {
|
||||
if (!$AUTH_ED25519_SO) {
|
||||
skip No auth_ed25519 plugin;
|
||||
}
|
||||
let $AUTH_PLUGIN = ed25519;
|
||||
let $CLIENT_PLUGIN=client_ed25519;
|
||||
}
|
||||
|
||||
if (!$CLIENT_ED25519_SO) {
|
||||
skip No client_ed25519 plugin;
|
||||
if ($MTR_COMBINATION_PARSEC) {
|
||||
if (!$AUTH_PARSEC_SO) {
|
||||
skip No auth_parsec plugin;
|
||||
}
|
||||
let $AUTH_PLUGIN = parsec;
|
||||
let $CLIENT_PLUGIN=parsec;
|
||||
}
|
||||
|
||||
--replace_result $CLIENT_PLUGIN CLIENT_PLUGIN
|
||||
--error ER_CANT_OPEN_LIBRARY,ER_CANT_FIND_DL_ENTRY
|
||||
install soname 'client_ed25519';
|
||||
eval install soname '$CLIENT_PLUGIN';
|
||||
if ($errno == 1126) {
|
||||
# this happens in bintars when C/C is linked with gnutls
|
||||
skip client_ed25519 contains unresolved symbols;
|
||||
skip $CLIENT_PLUGIN is not found or contains unresolved symbols;
|
||||
}
|
||||
|
||||
source include/master-slave.inc;
|
||||
|
||||
sync_slave_with_master;
|
||||
install soname 'auth_ed25519';
|
||||
# create a user for replication with ed25519 auth plugin
|
||||
--replace_result $AUTH_PLUGIN PLUGIN
|
||||
eval install soname 'auth_$AUTH_PLUGIN';
|
||||
# create a user for replication with auth plugin
|
||||
connection master;
|
||||
install soname 'auth_ed25519';
|
||||
create user rpluser@'%' identified via ed25519 using PASSWORD('rpl_pass');
|
||||
--replace_result $AUTH_PLUGIN plugin
|
||||
eval install soname 'auth_$AUTH_PLUGIN';
|
||||
|
||||
--replace_result $AUTH_PLUGIN PLUGIN
|
||||
eval create user rpluser@'%' identified via $AUTH_PLUGIN using PASSWORD('rpl_pass');
|
||||
grant replication slave on *.* to rpluser@'%';
|
||||
connection master;
|
||||
sync_slave_with_master;
|
||||
# Set the slave to connect using the user created with the ed25519 plugin for replication
|
||||
# Set the slave to connect using the user created with the auth plugin for replication
|
||||
source include/stop_slave.inc;
|
||||
--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR
|
||||
change master to master_user='rpluser', master_password='rpl_pass';
|
||||
|
@ -35,7 +49,9 @@ change master to master_user='root', master_password='';
|
|||
source include/start_slave.inc;
|
||||
source include/stop_slave.inc;
|
||||
drop user rpluser@'%';
|
||||
uninstall soname 'auth_ed25519';
|
||||
--replace_result $AUTH_PLUGIN plugin
|
||||
eval uninstall soname 'auth_$AUTH_PLUGIN';
|
||||
connection master;
|
||||
drop user rpluser@'%';
|
||||
uninstall soname 'auth_ed25519';
|
||||
--replace_result $AUTH_PLUGIN plugin
|
||||
eval uninstall soname 'auth_$AUTH_PLUGIN';
|
||||
|
|
4
plugin/auth_parsec/CMakeLists.txt
Normal file
4
plugin/auth_parsec/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
IF (HAVE_evp_pkey)
|
||||
ADD_DEFINITIONS(${SSL_DEFINES})
|
||||
MYSQL_ADD_PLUGIN(auth_parsec server_parsec.cc LINK_LIBRARIES ${SSL_LIBRARIES})
|
||||
ENDIF()
|
309
plugin/auth_parsec/server_parsec.cc
Normal file
309
plugin/auth_parsec/server_parsec.cc
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
Copyright (c) 2024, MariaDB plc
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/err.h>
|
||||
#ifdef HAVE_WOLFSSL
|
||||
#include <openssl/ed25519.h>
|
||||
#include <wolfcrypt/ed25519.h>
|
||||
#endif
|
||||
|
||||
#include <mysql/plugin_auth.h>
|
||||
#include <mysql/plugin.h>
|
||||
#include "scope.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
typedef unsigned char uchar;
|
||||
constexpr size_t CHALLENGE_SCRAMBLE_LENGTH= 32;
|
||||
constexpr size_t CHALLENGE_SALT_LENGTH= 18;
|
||||
constexpr size_t ED25519_SIG_LENGTH= 64;
|
||||
constexpr size_t ED25519_KEY_LENGTH= 32;
|
||||
constexpr size_t PBKDF2_HASH_LENGTH= ED25519_KEY_LENGTH;
|
||||
constexpr size_t CLIENT_RESPONSE_LENGTH= CHALLENGE_SCRAMBLE_LENGTH
|
||||
+ ED25519_SIG_LENGTH;
|
||||
|
||||
constexpr size_t base64_length(size_t input_length)
|
||||
{
|
||||
return ((input_length + 2) / 3) * 4; // with padding
|
||||
}
|
||||
|
||||
constexpr size_t base64_length_raw(size_t input_length)
|
||||
{
|
||||
return (input_length * 4 + 2) / 3; // no padding
|
||||
}
|
||||
|
||||
struct alignas(1) Client_signed_response
|
||||
{
|
||||
union {
|
||||
struct {
|
||||
uchar client_scramble[CHALLENGE_SCRAMBLE_LENGTH];
|
||||
uchar signature[ED25519_SIG_LENGTH];
|
||||
};
|
||||
uchar start[1];
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(Client_signed_response) == CLIENT_RESPONSE_LENGTH,
|
||||
"Client_signed_response is not aligned.");
|
||||
|
||||
struct alignas(1) Passwd_as_stored
|
||||
{
|
||||
char algorithm;
|
||||
uchar iterations;
|
||||
char colon;
|
||||
char salt[base64_length_raw(CHALLENGE_SALT_LENGTH)];
|
||||
char colon2;
|
||||
char pub_key[base64_length_raw(ED25519_KEY_LENGTH)];
|
||||
};
|
||||
|
||||
struct alignas(1) Passwd_in_memory
|
||||
{
|
||||
char algorithm;
|
||||
uchar iterations;
|
||||
uchar salt[CHALLENGE_SALT_LENGTH];
|
||||
uchar pub_key[ED25519_KEY_LENGTH];
|
||||
};
|
||||
|
||||
int print_ssl_error()
|
||||
{
|
||||
char buf[512];
|
||||
unsigned long err= ERR_get_error();
|
||||
ERR_error_string_n(err, buf, sizeof buf);
|
||||
my_printf_error(err, "parsec: %s", ME_ERROR_LOG_ONLY, buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
int compute_derived_key(const char* password, size_t pass_len,
|
||||
const Passwd_in_memory *params, uchar *derived_key)
|
||||
{
|
||||
assert(params->algorithm == 'P');
|
||||
int ret = PKCS5_PBKDF2_HMAC(password, (int)pass_len, params->salt,
|
||||
sizeof(params->salt), 1024 << params->iterations,
|
||||
EVP_sha512(), PBKDF2_HASH_LENGTH, derived_key);
|
||||
if(ret == 0)
|
||||
return print_ssl_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int verify_ed25519(const uchar *public_key, const uchar *signature,
|
||||
const uchar *message, size_t message_len)
|
||||
{
|
||||
#ifdef HAVE_WOLFSSL
|
||||
int res= wolfSSL_ED25519_verify(message, (unsigned)message_len,
|
||||
public_key, ED25519_KEY_LENGTH,
|
||||
signature, ED25519_SIG_LENGTH);
|
||||
return res != WOLFSSL_SUCCESS;
|
||||
#else
|
||||
int ret= 1;
|
||||
EVP_MD_CTX *mdctx= EVP_MD_CTX_new();
|
||||
EVP_PKEY *pkey= EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL,
|
||||
public_key, 32);
|
||||
|
||||
if (pkey && mdctx && EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey))
|
||||
ret= !EVP_DigestVerify(mdctx, signature, ED25519_SIG_LENGTH,
|
||||
message, message_len);
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
EVP_PKEY_free(pkey);
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
int ed25519_derive_public_key(const uchar *raw_private_key, uchar *pub_key)
|
||||
{
|
||||
#ifdef HAVE_WOLFSSL
|
||||
ed25519_key key;
|
||||
|
||||
int ret = wc_ed25519_init(&key);
|
||||
if (ret != 0)
|
||||
return print_ssl_error();
|
||||
|
||||
SCOPE_EXIT([&key](){ wc_ed25519_free(&key); });
|
||||
|
||||
ret = wc_ed25519_import_private_only(raw_private_key, ED25519_KEY_LENGTH,
|
||||
&key);
|
||||
if (ret != 0)
|
||||
return print_ssl_error();
|
||||
|
||||
ret = wc_ed25519_make_public(&key, pub_key, ED25519_KEY_LENGTH);
|
||||
if (ret != 0)
|
||||
return print_ssl_error();
|
||||
|
||||
return false;
|
||||
#else
|
||||
EVP_PKEY *pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL,
|
||||
raw_private_key,
|
||||
ED25519_KEY_LENGTH);
|
||||
bool res= pkey != NULL;
|
||||
size_t len= ED25519_KEY_LENGTH;
|
||||
if (pkey)
|
||||
res= EVP_PKEY_get_raw_public_key(pkey, pub_key, &len); // 1 == success
|
||||
|
||||
if (!res)
|
||||
print_ssl_error();
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
return !res;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
int hash_password(const char *password, size_t password_length,
|
||||
char *hash, size_t *hash_length)
|
||||
{
|
||||
auto stored= (Passwd_as_stored*)hash;
|
||||
if (*hash_length < sizeof(*stored) + 2)
|
||||
return 1;
|
||||
|
||||
Passwd_in_memory memory;
|
||||
memory.algorithm= 'P';
|
||||
memory.iterations= 0;
|
||||
my_random_bytes(memory.salt, sizeof(memory.salt));
|
||||
|
||||
uchar derived_key[PBKDF2_HASH_LENGTH];
|
||||
if (compute_derived_key(password, password_length, &memory, derived_key))
|
||||
return 1;
|
||||
|
||||
if (ed25519_derive_public_key(derived_key, memory.pub_key))
|
||||
return 1;
|
||||
|
||||
stored->algorithm= memory.algorithm;
|
||||
stored->iterations= memory.iterations + '0';
|
||||
my_base64_encode(memory.salt, sizeof(memory.salt), stored->salt);
|
||||
my_base64_encode(memory.pub_key, sizeof(memory.pub_key), stored->pub_key);
|
||||
stored->colon= stored->colon2= ':';
|
||||
|
||||
*hash_length = sizeof *stored;
|
||||
hash[*hash_length]= 0; // safety
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int digest_to_binary(const char *hash, size_t hash_length,
|
||||
unsigned char *out, size_t *out_length)
|
||||
{
|
||||
auto stored= (Passwd_as_stored*)hash;
|
||||
auto memory= (Passwd_in_memory*)out;
|
||||
|
||||
if (hash_length != sizeof (*stored) || *out_length < sizeof(*memory) ||
|
||||
stored->algorithm != 'P' ||
|
||||
stored->iterations < '0' || stored->iterations > '3' ||
|
||||
stored->colon != ':' || stored->colon2 != ':')
|
||||
return 1;
|
||||
|
||||
*out_length = sizeof(*memory);
|
||||
memory->algorithm= stored->algorithm;
|
||||
memory->iterations= stored->iterations - '0';
|
||||
|
||||
static_assert(base64_length(CHALLENGE_SALT_LENGTH) == base64_length_raw(CHALLENGE_SALT_LENGTH),
|
||||
"Salt is base64-aligned");
|
||||
if (my_base64_decode(stored->salt, base64_length(CHALLENGE_SALT_LENGTH),
|
||||
memory->salt, NULL, 0) < 0)
|
||||
return 1;
|
||||
|
||||
char buf[base64_length(ED25519_KEY_LENGTH)+1];
|
||||
constexpr int pad= (int)base64_length(ED25519_KEY_LENGTH)
|
||||
- (int)base64_length_raw(ED25519_KEY_LENGTH);
|
||||
static_assert(pad > 0, "base64 length calculation check");
|
||||
memcpy(buf, stored->pub_key, base64_length_raw(ED25519_KEY_LENGTH));
|
||||
memset(buf + base64_length_raw(ED25519_KEY_LENGTH), '=', pad);
|
||||
if (my_base64_decode(buf, base64_length(ED25519_KEY_LENGTH),
|
||||
memory->pub_key, NULL, 0) < 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uchar server[CHALLENGE_SCRAMBLE_LENGTH];
|
||||
uchar client[CHALLENGE_SCRAMBLE_LENGTH];
|
||||
};
|
||||
uchar start[1];
|
||||
} scramble_pair;
|
||||
|
||||
my_random_bytes(scramble_pair.server, CHALLENGE_SCRAMBLE_LENGTH);
|
||||
|
||||
if (vio->write_packet(vio, scramble_pair.server, sizeof(scramble_pair.server)))
|
||||
return CR_ERROR;
|
||||
|
||||
// Begin with reading the handshake packet. It should be empty (for now).
|
||||
uchar *dummy;
|
||||
int bytes_read= vio->read_packet(vio, &dummy);
|
||||
if (bytes_read != 0)
|
||||
return CR_ERROR;
|
||||
|
||||
auto passwd= (Passwd_in_memory*)info->auth_string;
|
||||
|
||||
if (vio->write_packet(vio, (uchar*)info->auth_string, 2 + CHALLENGE_SALT_LENGTH))
|
||||
return CR_ERROR;
|
||||
|
||||
Client_signed_response *client_response;
|
||||
bytes_read= vio->read_packet(vio, (uchar**)&client_response);
|
||||
if (bytes_read < 0)
|
||||
return CR_ERROR;
|
||||
if (bytes_read != sizeof *client_response)
|
||||
return CR_AUTH_HANDSHAKE;
|
||||
|
||||
memcpy(scramble_pair.client, client_response->client_scramble,
|
||||
CHALLENGE_SCRAMBLE_LENGTH);
|
||||
|
||||
if (verify_ed25519(passwd->pub_key, client_response->signature,
|
||||
scramble_pair.start, sizeof(scramble_pair)))
|
||||
return CR_AUTH_HANDSHAKE;
|
||||
|
||||
return CR_OK;
|
||||
}
|
||||
|
||||
static struct st_mysql_auth info =
|
||||
{
|
||||
MYSQL_AUTHENTICATION_INTERFACE_VERSION,
|
||||
"parsec",
|
||||
auth,
|
||||
hash_password,
|
||||
digest_to_binary
|
||||
};
|
||||
|
||||
|
||||
maria_declare_plugin(auth_parsec)
|
||||
{
|
||||
MYSQL_AUTHENTICATION_PLUGIN,
|
||||
&info,
|
||||
"parsec",
|
||||
"Nikita Maliavin",
|
||||
"Password Authentication using Response Signed with Elliptic Curve",
|
||||
PLUGIN_LICENSE_GPL,
|
||||
NULL,
|
||||
NULL,
|
||||
0x0100,
|
||||
NULL,
|
||||
NULL,
|
||||
"1.0",
|
||||
MariaDB_PLUGIN_MATURITY_BETA
|
||||
}
|
||||
maria_declare_plugin_end;
|
Loading…
Reference in a new issue