Skip to content

Commit 7f66ff9

Browse files
committed
Add client_ed25519 authentication
- Implements the necessary client code for [ed25519 authentication](https://mariadb.com/kb/en/authentication-plugin-ed25519/). - Add a test directly from the reference implementation to verify it works. - A continuation of #1220, but doesn't use CGO. - This patch uses filippo.io/edwards25519 to implement the crypto bits. The standard library `crypto/ed25519` cannot be used as MariaDB chose a scheme that is simply not compatible with what the standard library provides.
1 parent 98d7289 commit 7f66ff9

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

‎auth.go‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import (
1313
"crypto/rsa"
1414
"crypto/sha1"
1515
"crypto/sha256"
16+
"crypto/sha512"
1617
"crypto/x509"
1718
"encoding/pem"
1819
"fmt"
1920
"sync"
21+
22+
"filippo.io/edwards25519"
2023
)
2124

2225
// server pub keys registry
@@ -225,6 +228,44 @@ func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte,
225228
returnrsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil)
226229
}
227230

231+
// Derived from https://github.com/MariaDB/server/blob/d8e6bb00888b1f82c031938f4c8ac5d97f6874c3/plugin/auth_ed25519/ref10/sign.c
232+
funcdoEd25519Auth(scramble []byte, passwordstring) ([]byte, error){
233+
h:=sha512.Sum512([]byte(password))
234+
235+
s, err:=edwards25519.NewScalar().SetBytesWithClamping(h[:32])
236+
iferr!=nil{
237+
returnnil, err
238+
}
239+
240+
nonceHash:=sha512.New()
241+
nonceHash.Write(h[32:])
242+
nonceHash.Write(scramble)
243+
nonce:=nonceHash.Sum(nil)
244+
245+
r, err:=edwards25519.NewScalar().SetUniformBytes(nonce)
246+
iferr!=nil{
247+
returnnil, err
248+
}
249+
R:= (&edwards25519.Point{}).ScalarBaseMult(r)
250+
251+
A:= (&edwards25519.Point{}).ScalarBaseMult(s)
252+
253+
kHash:=sha512.New()
254+
kHash.Write(R.Bytes())
255+
kHash.Write(A.Bytes())
256+
kHash.Write(scramble)
257+
k:=kHash.Sum(nil)
258+
259+
K, err:=edwards25519.NewScalar().SetUniformBytes(k)
260+
iferr!=nil{
261+
returnnil, err
262+
}
263+
264+
S:=K.MultiplyAdd(K, s, r)
265+
266+
returnappend(R.Bytes(), S.Bytes()...), nil
267+
}
268+
228269
func (mc*mysqlConn) sendEncryptedPassword(seed []byte, pub*rsa.PublicKey) error{
229270
enc, err:=encryptPassword(mc.cfg.Passwd, seed, pub)
230271
iferr!=nil{
@@ -290,6 +331,13 @@ func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error){
290331
enc, err:=encryptPassword(mc.cfg.Passwd, authData, pubKey)
291332
returnenc, err
292333

334+
case"client_ed25519":
335+
iflen(authData) !=32{
336+
returnnil, ErrMalformPkt
337+
}
338+
339+
returndoEd25519Auth(authData, mc.cfg.Passwd)
340+
293341
default:
294342
mc.cfg.Logger.Print("unknown auth plugin:", plugin)
295343
returnnil, ErrUnknownPlugin

‎auth_test.go‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,3 +1328,54 @@ func TestAuthSwitchSHA256PasswordSecure(t *testing.T){
13281328
t.Errorf("got unexpected data: %v", conn.written)
13291329
}
13301330
}
1331+
1332+
// Derived from https://github.com/MariaDB/server/blob/6b2287fff23fbdc362499501c562f01d0d2db52e/plugin/auth_ed25519/ed25519-t.c
1333+
funcTestEd25519Auth(t*testing.T){
1334+
conn, mc:=newRWMockConn(1)
1335+
mc.cfg.User="root"
1336+
mc.cfg.Passwd="foobar"
1337+
1338+
authData:= []byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
1339+
plugin:="client_ed25519"
1340+
1341+
// Send Client Authentication Packet
1342+
authResp, err:=mc.auth(authData, plugin)
1343+
iferr!=nil{
1344+
t.Fatal(err)
1345+
}
1346+
err=mc.writeHandshakeResponsePacket(authResp, plugin)
1347+
iferr!=nil{
1348+
t.Fatal(err)
1349+
}
1350+
1351+
// check written auth response
1352+
authRespStart:=4+4+4+1+23+len(mc.cfg.User) +1
1353+
authRespEnd:=authRespStart+1+len(authResp)
1354+
writtenAuthRespLen:=conn.written[authRespStart]
1355+
writtenAuthResp:=conn.written[authRespStart+1 : authRespEnd]
1356+
expectedAuthResp:= []byte{
1357+
232, 61, 201, 63, 67, 63, 51, 53, 86, 73, 238, 35, 170, 117, 146,
1358+
214, 26, 17, 35, 9, 8, 132, 245, 141, 48, 99, 66, 58, 36, 228, 48,
1359+
84, 115, 254, 187, 168, 88, 162, 249, 57, 35, 85, 79, 238, 167, 106,
1360+
68, 117, 56, 135, 171, 47, 20, 14, 133, 79, 15, 229, 124, 160, 176,
1361+
100, 138, 14,
1362+
}
1363+
ifwrittenAuthRespLen!=64{
1364+
t.Fatalf("expected 64 bytes from client, got %d", writtenAuthRespLen)
1365+
}
1366+
if!bytes.Equal(writtenAuthResp, expectedAuthResp){
1367+
t.Fatalf("auth response did not match expected value:\n%v\n%v", writtenAuthResp, expectedAuthResp)
1368+
}
1369+
conn.written=nil
1370+
1371+
// auth response
1372+
conn.data= []byte{
1373+
7, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, // OK
1374+
}
1375+
conn.maxReads=1
1376+
1377+
// Handle response to auth packet
1378+
iferr:=mc.handleAuthResult(authData, plugin); err!=nil{
1379+
t.Errorf("got error: %v", err)
1380+
}
1381+
}

‎go.mod‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
modulegithub.com/go-sql-driver/mysql
22

33
go1.18
4+
5+
requirefilippo.io/edwards25519v1.0.0

‎go.sum‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
filippo.io/edwards25519v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
2+
filippo.io/edwards25519v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=

0 commit comments

Comments
(0)