diff --git a/net/packet/util.go b/net/packet/util.go index 68935e1..536a99c 100644 --- a/net/packet/util.go +++ b/net/packet/util.go @@ -256,7 +256,7 @@ func (t Tuple) ReadFrom(r io.Reader) (n int64, err error) { for i, v := range t { nn, err := v.(FieldDecoder).ReadFrom(r) if err != nil { - return n, fmt.Errorf("decode tuple[%d] error: %w", i, err) + return n, fmt.Errorf("decode tuple[%d] %T error: %w", i, v, err) } n += nn } diff --git a/server/auth/auth.go b/server/auth/auth.go index 3efdf39..8a922ca 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -2,12 +2,10 @@ package auth import ( "bytes" - "crypto" "crypto/aes" "crypto/rand" "crypto/rsa" "crypto/sha1" - "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" @@ -27,26 +25,26 @@ import ( const verifyTokenLen = 16 // Encrypt a connection, with authentication -func Encrypt(conn *net.Conn, name string, profilePubKey *rsa.PublicKey) (*Resp, error) { - // generate keys - key, err := rsa.GenerateKey(rand.Reader, 1024) +func Encrypt(conn *net.Conn, name string, serverKey *rsa.PrivateKey) (*Resp, error) { + publicKey, err := x509.MarshalPKIXPublicKey(&serverKey.PublicKey) if err != nil { return nil, err } - publicKey, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + verifyToken := make([]byte, verifyTokenLen) + _, err = rand.Read(verifyToken) if err != nil { return nil, err } // encryption request - nonce, err := encryptionRequest(conn, publicKey) + err = encryptionRequest(conn, publicKey, verifyToken) if err != nil { return nil, err } // encryption response - SharedSecret, err := encryptionResponse(conn, profilePubKey, nonce, key) + SharedSecret, err := encryptionResponse(conn, serverKey, verifyToken) if err != nil { return nil, err } @@ -70,22 +68,16 @@ func Encrypt(conn *net.Conn, name string, profilePubKey *rsa.PublicKey) (*Resp, return resp, nil } -func encryptionRequest(conn *net.Conn, publicKey []byte) ([]byte, error) { - var verifyToken [verifyTokenLen]byte - _, err := rand.Read(verifyToken[:]) - if err != nil { - return nil, err - } - err = conn.WritePacket(pk.Marshal( +func encryptionRequest(conn *net.Conn, publicKey, verifyToken []byte) error { + return conn.WritePacket(pk.Marshal( packetid.LoginEncryptionRequest, pk.String(""), pk.ByteArray(publicKey), - pk.ByteArray(verifyToken[:]), + pk.ByteArray(verifyToken), )) - return verifyToken[:], err } -func encryptionResponse(conn *net.Conn, profilePubKey *rsa.PublicKey, nonce []byte, key *rsa.PrivateKey) ([]byte, error) { +func encryptionResponse(conn *net.Conn, serverKey *rsa.PrivateKey, verifyToken []byte) ([]byte, error) { var p pk.Packet err := conn.ReadPacket(&p) if err != nil { @@ -95,54 +87,29 @@ func encryptionResponse(conn *net.Conn, profilePubKey *rsa.PublicKey, nonce []by return nil, fmt.Errorf("0x%02X is not Encryption Response", p.ID) } - r := bytes.NewReader(p.Data) - var ESharedSecret pk.ByteArray - if _, err = ESharedSecret.ReadFrom(r); err != nil { - return nil, err - } - var isNonce pk.Boolean - if _, err = isNonce.ReadFrom(r); err != nil { - return nil, err - } - if isNonce { - var nonce2 pk.ByteArray - _, err = nonce2.ReadFrom(r) - if err != nil { - return nil, err - } + var keyBytes pk.ByteArray + var encryptedVerifyToken pk.ByteArray - nonce2, err = rsa.DecryptPKCS1v15(rand.Reader, key, nonce2) - if err != nil { - return nil, err - } - if !bytes.Equal(nonce, nonce2) { - return nil, errors.New("nonce not match") - } - - } else { - var salt pk.Long - var signature pk.ByteArray - _, err = pk.Tuple{&salt, &signature}.ReadFrom(r) - if err != nil { - return nil, err - } - - hash := sha256.New() - unwrap(hash.Write(nonce)) - unwrap(salt.WriteTo(hash)) - err := rsa.VerifyPKCS1v15(profilePubKey, crypto.SHA256, hash.Sum(nil), signature) - if err != nil { - return nil, err - } - } - - // confirm to verify token - SharedSecret, err := rsa.DecryptPKCS1v15(rand.Reader, key, ESharedSecret) + err = p.Scan(&keyBytes, &encryptedVerifyToken) if err != nil { return nil, err } - return SharedSecret, nil + // confirm to verify token + decryptedVerifyToken, err := rsa.DecryptPKCS1v15(rand.Reader, serverKey, encryptedVerifyToken) + if err != nil { + return nil, err + } else if !bytes.Equal(verifyToken, decryptedVerifyToken) { + return nil, errors.New("verifyToken not match") + } + + // get sharedSecret + sharedSecret, err := rsa.DecryptPKCS1v15(rand.Reader, serverKey, keyBytes) + if err != nil { + return nil, err + } + + return sharedSecret, nil } func authentication(name, hash string) (*Resp, error) { diff --git a/server/login.go b/server/login.go index acd0cf3..498270c 100644 --- a/server/login.go +++ b/server/login.go @@ -1,7 +1,11 @@ package server import ( + "crypto/rand" + "crypto/rsa" "fmt" + "sync" + "sync/atomic" "github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/data/packetid" @@ -48,8 +52,40 @@ type MojangLoginHandler struct { // (e.g. blacklist or is server full). // This is optional field and can be set to nil. LoginChecker + + // PrivateKey is the key used by encrypt the connection. + privateKey atomic.Pointer[rsa.PrivateKey] + lockPrivateKey sync.Mutex } +func (d *MojangLoginHandler) getPrivateKey() (key *rsa.PrivateKey, err error) { + key = d.privateKey.Load() + if key != nil { + return + } + + d.lockPrivateKey.Lock() + defer d.lockPrivateKey.Unlock() + + key = d.privateKey.Load() + if key == nil { + key, err = rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + return + } + d.privateKey.Store(key) + } + return +} + +/* + var verifyToken [verifyTokenLen]byte + _, err := rand.Read(verifyToken[:]) + if err != nil { + return nil, err + } +*/ + // AcceptLogin implement LoginHandler for MojangLoginHandler func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name string, id uuid.UUID, profilePubKey *auth.PublicKey, properties []auth.Property, err error) { // login start @@ -63,14 +99,9 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s return } - var ( - pubKey pk.Option[auth.PublicKey, *auth.PublicKey] - profileUUID pk.Option[pk.UUID, *pk.UUID] // ignored - ) err = p.Scan( (*pk.String)(&name), // decode username as pk.String - &pubKey, - &profileUUID, + (*pk.UUID)(&id), ) if err != nil { return @@ -78,20 +109,14 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s // auth if d.OnlineMode { - if pubKey.Has { - if !pubKey.Val.Verify() { - err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.invalid_public_key_signature")} - return - } - profilePubKey = &pubKey.Val - } else if d.EnforceSecureProfile { - err = LoginFailErr{reason: chat.TranslateMsg("multiplayer.disconnect.missing_public_key")} + var serverKey *rsa.PrivateKey + serverKey, err = d.getPrivateKey() + if err != nil { return } - var resp *auth.Resp // Auth, Encrypt - resp, err = auth.Encrypt(conn, name, pubKey.Val.PubKey) + resp, err = auth.Encrypt(conn, name, serverKey) if err != nil { return }