Add chat session

This commit is contained in:
Tnze
2023-01-01 13:17:07 +08:00
parent 77857a1a85
commit fa83b762bf
15 changed files with 244 additions and 25 deletions

74
yggdrasil/user/pubkey.go Normal file
View File

@ -0,0 +1,74 @@
package user
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"errors"
"io"
"time"
pk "github.com/Tnze/go-mc/net/packet"
)
type PublicKey struct {
ExpiresAt time.Time
PubKey *rsa.PublicKey
Signature []byte
}
func (p PublicKey) WriteTo(w io.Writer) (n int64, err error) {
pubKeyEncoded, err := x509.MarshalPKIXPublicKey(p.PubKey)
if err != nil {
return 0, err
}
return pk.Tuple{
pk.Long(p.ExpiresAt.UnixMilli()),
pk.ByteArray(pubKeyEncoded),
pk.ByteArray(p.Signature),
}.WriteTo(w)
}
func (p *PublicKey) ReadFrom(r io.Reader) (n int64, err error) {
var (
ExpiresAt pk.Long
PubKey pk.ByteArray
Signature pk.ByteArray
)
n, err = pk.Tuple{
&ExpiresAt,
&PubKey,
&Signature,
}.ReadFrom(r)
if err != nil {
return n, err
}
p.ExpiresAt = time.UnixMilli(int64(ExpiresAt))
pubKey, err := x509.ParsePKIXPublicKey(PubKey)
if err != nil {
return n, err
}
if key, ok := pubKey.(*rsa.PublicKey); !ok {
return n, errors.New("expect RSA public key")
} else {
p.PubKey = key
}
p.Signature = Signature
return n, nil
}
func (p *PublicKey) Verify() bool {
if p.ExpiresAt.Before(time.Now()) {
return false
}
encoded, err := x509.MarshalPKIXPublicKey(p.PubKey)
if err != nil {
return false
}
return VerifySignature(encoded, p.Signature)
}
func (p *PublicKey) VerifyMessage(hash, signature []byte) error {
return rsa.VerifyPKCS1v15(p.PubKey, crypto.SHA256, hash, signature)
}

View File

@ -0,0 +1,92 @@
package user
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
_ "embed"
"encoding/base64"
"io"
)
//go:embed yggdrasil_session_pubkey.der
var pubKeyBytes []byte
var pubKey = unwrap(x509.ParsePKIXPublicKey(pubKeyBytes)).(*rsa.PublicKey)
// VerifySignature has the same functional as
// net.minecraft.world.entity.player.ProfilePublicKey.Data#validateSignature
func VerifySignature(profilePubKey, signature []byte) bool {
hash := sha256.New()
unwrap(hash.Write([]byte("-----BEGIN RSA PRIVATE KEY-----\n")))
breaker := lineBreaker{out: hash}
enc := base64.NewEncoder(base64.StdEncoding, &breaker)
unwrap(enc.Write(profilePubKey))
must(enc.Close())
must(breaker.Close())
unwrap(hash.Write([]byte("\n-----END RSA PRIVATE KEY-----\n")))
return rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hash.Sum(nil), signature) != nil
}
const pemLineLength = 76
var nl = []byte{'\n'}
type lineBreaker struct {
line [pemLineLength]byte
used int
out io.Writer
}
func (l *lineBreaker) Write(b []byte) (n int, err error) {
if l.used+len(b) < pemLineLength {
copy(l.line[l.used:], b)
l.used += len(b)
return len(b), nil
}
n, err = l.out.Write(l.line[0:l.used])
if err != nil {
return
}
excess := pemLineLength - l.used
l.used = 0
n1, err := l.out.Write(b[0:excess])
if err != nil {
return n + n1, err
}
n2, err := l.out.Write(nl)
if err != nil {
return n + n1 + n2, err
}
n3, err := l.Write(b[excess:])
return n1 + n2 + n3, err
}
func (l *lineBreaker) Close() (err error) {
if l.used > 0 {
_, err = l.out.Write(l.line[0:l.used])
if err != nil {
return
}
_, err = l.out.Write(nl)
}
return
}
func must(err error) {
if err != nil {
panic(err)
}
}
func unwrap[T any](v T, err error) T {
if err != nil {
panic(err)
}
return v
}

Binary file not shown.