@@ -113,6 +113,66 @@ def _hash_password_323(password):
113113 return struct .pack (">LL" , r1 , r2 )
114114
115115
116+ # MariaDB's client_ed25519-plugin
117+ # https://mariadb.com/kb/en/library/connection/#client_ed25519-plugin
118+
119+ _nacl_bindings = False
120+
121+
122+ def _init_nacl ():
123+ global _nacl_bindings
124+ try :
125+ from nacl import bindings
126+ _nacl_bindings = bindings
127+ except ImportError :
128+ raise RuntimeError ("'pynacl' package is required for ed25519_password auth method" )
129+
130+
131+ def _scalar_clamp (s32 ):
132+ ba = bytearray (s32 )
133+ ba0 = bytes (bytearray ([ba [0 ] & 248 ]))
134+ ba31 = bytes (bytearray ([(ba [31 ] & 127 ) | 64 ]))
135+ return ba0 + bytes (s32 [1 :31 ]) + ba31
136+
137+
138+ def ed25519_password (password , scramble ):
139+ """Sign a random scramble with elliptic curve Ed25519.
140+
141+ Secret and public key are derived from password.
142+ """
143+ # variable names based on rfc8032 section-5.1.6
144+ #
145+ if not _nacl_bindings :
146+ _init_nacl ()
147+
148+ # h = SHA512(password)
149+ h = hashlib .sha512 (password ).digest ()
150+
151+ # s = prune(first_half(h))
152+ s = _scalar_clamp (h [:32 ])
153+
154+ # r = SHA512(second_half(h) || M)
155+ r = hashlib .sha512 (h [32 :] + scramble ).digest ()
156+
157+ # R = encoded point [r]B
158+ r = _nacl_bindings .crypto_core_ed25519_scalar_reduce (r )
159+ R = _nacl_bindings .crypto_scalarmult_ed25519_base_noclamp (r )
160+
161+ # A = encoded point [s]B
162+ A = _nacl_bindings .crypto_scalarmult_ed25519_base_noclamp (s )
163+
164+ # k = SHA512(R || A || M)
165+ k = hashlib .sha512 (R + A + scramble ).digest ()
166+
167+ # S = (k * s + r) mod L
168+ k = _nacl_bindings .crypto_core_ed25519_scalar_reduce (k )
169+ ks = _nacl_bindings .crypto_core_ed25519_scalar_mul (k , s )
170+ S = _nacl_bindings .crypto_core_ed25519_scalar_add (ks , r )
171+
172+ # signature = R || S
173+ return R + S
174+
175+
116176# sha256_password
117177
118178
0 commit comments