[MDEV-19217] ed25519 algorithm is not publicly available except for passwords that are 32 characters long Created: 2019-04-08  Updated: 2021-02-17  Resolved: 2021-02-17

Status: Closed
Project: MariaDB Server
Component/s: Authentication and Privilege System, Documentation
Affects Version/s: 10.2.23, 10.3.14
Fix Version/s: N/A

Type: Bug Priority: Minor
Reporter: Mike Bayer Assignee: Sergei Golubchik
Resolution: Won't Fix Votes: 0
Labels: auth_plugin, authentication


 Description   

We are attempting to implement ed25519 authentication for the PyMySQL Python client library, which implements the MySQL protocol using pure Python, no mysql/mariadb client linkage.

However, per the README, MariaDB has made their own implementation of ed25519 that uses the "password" as the secret key, accepting a password string of arbitrary length.

However, the most widely available ed25519 libraries pynacl (against libsodium) and python-ed25519 (against SUPERCOP) both accept secret keys that are exactly 32 or 64 bytes long. If the key is not either of those lengths, the algorithm does not work.

For example, I made a quick auth plugin in PyMySQL using python-ed25519 like this:

def ed25519_password(password, scramble):
 
    import ed25519
 
    signing_key = ed25519.SigningKey(password)
    sig = signing_key.sign(scramble)
    return sig

the above works if the password is exactly 32 characters long. Otherwise, it fails. I also tried removing python-ed25519's check for 32-byte secret key so that it produces an answer, but the signature it provides fails against MariaDB server.

There is no publicly available implementation of MariaDB's fork of this encryption code, it is of course open source within MariaDB but is not exported as an available library function.

I am of course not a cryptology person and I am sure there are lots of interesting mathematical details as to why MariaDB's algo doesn't need a 32 byte key and why these other ones do, etc., however, the point of encryption libraries is that the end users should never be trying to hand roll this stuff themselves in any case, since any kind of "tweaks" to an algorithm by nature render it insecure until it is again validated by an open community of crypto experts. We non-crypto's need to have access to high level functions that will do the signatures / signing / etc correctly.

The community could use a little more guidance in what we are supposed to do here. thanks!



 Comments   
Comment by Sergei Golubchik [ 2019-04-08 ]

You're right, this should to be documented in the manual.

The short version is here: if you look at the original ed25519 from SUPERCOP, it has a crypto_sign_keypair() function that generates a pair of keys, pk and sk. It starts from generating 32 random bytes — this is the sk. And doing SHA512 on that.

In our case, we use the user password instead of random bytes, and start from SHA512("user password").

Comment by Sergei Golubchik [ 2019-04-08 ]

The unfortunate consequences of this design is that neither libsodium nor SUPERCOP work for you out of the box, it basically needs a patched crypto_sign_keypair() unless the password is exactly 32 bytes long.

Comment by Mike Bayer [ 2019-04-08 ]

yes I tried to understand the impact of SHA512 but it seems like these libraries are already doing a lot of SHA512 internally on the key as well, so I didn't see a way of following that without understanding the whole thing: https://github.com/pyca/pynacl/blob/master/src/libsodium/src/libsodium/crypto_sign/ed25519/ref10/sign.c#L65

The quickest way this could be done would be if mariadb could allow their crypto_sign() function to be part of the client API, then we could build a Python binding on that and import it from PyMySQL. OR if you could convince libsodium to add a new function (and that's where we'd get to see if they have some kind of issue with this change).

Comment by Mike Bayer [ 2019-04-08 ]

that is, PyMySQL can import some other package we put on pypi that has the thing here written natively, but PyMySQL itself can't package any C code directly.

Comment by Sergei Golubchik [ 2019-04-10 ]

I can try to provide crypto_sign() or something, but how would it help you? PyMySQL is a pure-python library, you don't link with libmariadb.

I can do a pull request for python-ed25519, but I don't know if there's any chance of it being accepted.

Comment by Mike Bayer [ 2019-04-10 ]

if you look in https://github.com/PyMySQL/PyMySQL/blob/master/pymysql/_auth.py, PyMySQL imports native crypro libraries to help with auth. If you provided some kind of C function, we'd have to look into publishing an all-new package on pypi that provides just this function. I would try to see if this could be maintained underneath the PyMySQL organization on github of which I am a member. This new library would be an optional-only dependency for PyMySQL for the small portion of users that want to use mariadb ed25519 authentication.

Alternatively I noticed there's a mariadb-connector-python apparently, maybe having it exposed there, that would be weird though if PyMySQL relied on a different DBAPI in order to do auth

Agree I don't know the status of python-ed25519. having it be possible via pynacl/libsodium would be the best, if it could occur via some esoteric combination of lower level crypto functions present there, however per my colleague he claims this recent change https://github.com/jedisct1/libsodium/commit/d3976446a0c19aa9fe6f66523741126869a4990e means it is even less possible .

mariadb's decision to fork the algo makes this a tough call and I think having it be a public API function that we can publish Python bindings towards in an entirely standalone library might be the only real solution that has zero chance of breaking.

Comment by Mike Bayer [ 2019-04-15 ]

we may have come up with a patch for libsodium that can potentially get this working using that library directly, if libsodium accepts our patches. this would be a much better path to travel since no new libraries need to be written.

Comment by Mike Bayer [ 2019-04-15 ]

err libsodum -> pynacl

Comment by Damien Ciabrini [ 2019-05-05 ]

I've done more testing, and starting version 1.0.17,libsodium provides a new low-level crypto API that exposes the functions which are called by crypto_sign_keypair (scalar multiplication on the Ed25519 curve and modular arithmetic).

That means that we can now implement our own Ed25519 signature with arbitrary length passwords like what MariaDB auth_ed25519 does. I published a gist [1] to show what my libsodium-based implementation looks like.

I think we can close this issue now that we have a clean path forward with libsodium 1.0.17.

[1] https://gist.github.com/dciabrin/1295fa9900147ae1de0df9d9e106a278

Comment by Sergei Golubchik [ 2019-05-06 ]

Thanks for the info!

My current thinking (over the last couple of weeks) is that it was a mistake to "optimize" the plugin in a way that made it incompatible with the standard ed25519 implementations. But unfortunately I still don't see any solution for this that wouldn't invalidate all current users' passwords.

It's great to know that libsodium provided a workaround.

Comment by Mike Bayer [ 2019-05-07 ]

dciabrin where are we going to put that C code ?

Comment by Damien Ciabrini [ 2019-05-07 ]

Hey Mike, that was just for the sake of the example (and to convince myself that libsodium has what we need to implement auth_ed25519).
For our immediate use in OpenStack we'll probably do a similar python implementation that uses PyNaCl (py binding to libsodium)

Comment by Sergei Golubchik [ 2021-02-17 ]

I acknowledge, it was a mistake to deviate from the "standard" ed25519, the minor benefit wasn't worth the broken compatibility with 3rd party libraries.

Unfortunately, I don't see anything we can do now. In the next authentication plugin we'll know to avoid this.

If you have any idea how we can fix it without forcing all users to recreate their passwords — please do tell and we'll reopen this issue.

Generated at Thu Feb 08 08:49:56 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.