Skip to content

Commit 9eaef66

Browse files
committed
Work on support for 1.19 signed Login Start.
1 parent ca2f071 commit 9eaef66

12 files changed

+96
-31
lines changed

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { name as appName } from './app.json'
1111
global.Buffer = require('buffer').Buffer
1212
// global.process = require('process');
1313
global.process.env.NODE_ENV = __DEV__ ? 'development' : 'production'
14+
if (typeof BigInt === 'undefined') global.BigInt = require('big-integer')
1415

1516
// Needed so that 'stream-http' chooses the right default protocol.
1617
global.location = {

package-lock.json

+14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@react-navigation/native": "^6.0.2",
2020
"@react-navigation/native-stack": "^6.1.0",
2121
"assert": "^2.0.0",
22+
"big-integer": "^1.6.51",
2223
"browserify-zlib": "^0.2.0",
2324
"buffer": "^6.0.3",
2425
"events": "^3.3.0",

src/minecraft/api/mojang.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const joinMinecraftSessionUrl =
2+
'https://sessionserver.mojang.com/session/minecraft/join'
3+
const getPlayerCertificatesUrl =
4+
'https://api.minecraftservices.com/player/certificates'
5+
6+
export interface Certificate {
7+
keyPair: {
8+
// -----BEGIN RSA PUBLIC KEY-----\n ... \n-----END RSA PUBLIC KEY-----\n
9+
publicKey: string
10+
// -----BEGIN RSA PRIVATE KEY-----\n ... \n-----END RSA PRIVATE KEY-----\n
11+
privateKey: string
12+
}
13+
publicKeySignature: string // [Base64 string; signed data]
14+
refreshedAfter: string // 2022-04-29T16:11:32.174783069Z
15+
expiresAt: string // 2022-04-30T00:11:32.174783069Z
16+
}
17+
18+
export const joinMinecraftSession = async (
19+
accessToken: string,
20+
selectedProfile: string,
21+
serverId: string
22+
) =>
23+
await fetch(joinMinecraftSessionUrl, {
24+
body: JSON.stringify({ accessToken, selectedProfile, serverId }),
25+
headers: { 'content-type': 'application/json' },
26+
method: 'POST'
27+
})
28+
29+
export const getPlayerCertificates = async (accessToken: string) =>
30+
await fetch(getPlayerCertificatesUrl, {
31+
headers: { Authorization: 'Bearer ' + accessToken },
32+
method: 'POST'
33+
})

src/minecraft/connection.ts

+39-25
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
makeBaseCompressedPacket,
1616
makeBasePacket,
1717
Packet,
18+
PacketDataTypes,
1819
parseCompressedPacket,
1920
parsePacket
2021
} from './packet'
@@ -24,9 +25,9 @@ import {
2425
resolveHostname,
2526
generateSharedSecret,
2627
mcHexDigest,
27-
authUrl,
2828
protocolMap
2929
} from './utils'
30+
import { Certificate, joinMinecraftSession } from './api/mojang'
3031

3132
export interface ConnectionOptions {
3233
host: string
@@ -35,6 +36,7 @@ export interface ConnectionOptions {
3536
protocolVersion: number
3637
selectedProfile?: string
3738
accessToken?: string
39+
certificate?: Certificate
3840
}
3941

4042
export declare interface ServerConnection {
@@ -92,6 +94,33 @@ export class ServerConnection extends events.EventEmitter {
9294
}
9395
}
9496

97+
const getLoginPacket = (opts: ConnectionOptions) => {
98+
const data: PacketDataTypes[] = [opts.username]
99+
if (opts.protocolVersion >= protocolMap[1.19]) {
100+
data.push(!!opts.certificate)
101+
// TODO-1.19: Test if chat signing keys work.
102+
if (opts.certificate) {
103+
let buf = Buffer.alloc(8)
104+
buf.writeBigInt64BE(
105+
BigInt(new Date(opts.certificate.expiresAt).getTime())
106+
)
107+
data.push(buf)
108+
const pkData = opts.certificate.keyPair.publicKey
109+
.split('\n')
110+
.filter(line => !line.startsWith('-----'))
111+
.join('')
112+
.trim()
113+
buf = Buffer.from(pkData, 'base64')
114+
data.push(writeVarInt(buf.byteLength))
115+
data.push(buf)
116+
buf = Buffer.from(opts.certificate.publicKeySignature, 'base64')
117+
data.push(writeVarInt(buf.byteLength))
118+
data.push(buf)
119+
}
120+
}
121+
return concatPacketData(data)
122+
}
123+
95124
const initiateConnection = async (opts: ConnectionOptions) => {
96125
const [host, port] = await resolveHostname(opts.host, opts.port)
97126
return await new Promise<ServerConnection>((resolve, reject) => {
@@ -112,20 +141,10 @@ const initiateConnection = async (opts: ConnectionOptions) => {
112141
// Initialise Handshake with server.
113142
socket.write(makeBasePacket(0x00, concatPacketData(handshakeData)), () =>
114143
// Send Login Start packet.
115-
socket.write(
116-
makeBasePacket(
117-
0x00,
118-
concatPacketData(
119-
opts.protocolVersion < protocolMap[1.19]
120-
? [opts.username]
121-
: [opts.username, false] // TODO-1.19: Support chat signing keys.
122-
)
123-
),
124-
() => {
125-
resolved = true
126-
resolve(conn)
127-
}
128-
)
144+
socket.write(makeBasePacket(0x00, getLoginPacket(opts)), () => {
145+
resolved = true
146+
resolve(conn)
147+
})
129148
)
130149
})
131150
socket.on('close', () => {
@@ -197,16 +216,11 @@ const initiateConnection = async (opts: ConnectionOptions) => {
197216
sha1.update(publicKey) // Server's encoded public key from Encryption Request
198217
const hash = mcHexDigest(sha1.digest())
199218
// Send hash to Mojang servers.
200-
const body = JSON.stringify({
201-
accessToken,
202-
selectedProfile,
203-
serverId: hash
204-
})
205-
const req = await fetch(authUrl, {
206-
headers: { 'content-type': 'application/json' },
207-
method: 'POST',
208-
body
209-
})
219+
const req = await joinMinecraftSession(
220+
accessToken as string,
221+
selectedProfile as string,
222+
hash
223+
)
210224
if (!req.ok) {
211225
throw new Error('Mojang online mode network request failed')
212226
}

src/minecraft/utils.ts

-3
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ export const resolveHostname = async (
6161
} else return [hostname, port]
6262
}
6363

64-
// Online-mode cryptography utilities.
65-
export const authUrl = 'https://sessionserver.mojang.com/session/minecraft/join'
66-
6764
export const generateSharedSecret = async () =>
6865
await new Promise<Buffer>((resolve, reject) => {
6966
randomBytes(16, (err, buf) => {

src/screens/accounts/AccountScreen.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Dialog, { dialogStyles } from '../../components/Dialog'
99
import useDarkMode from '../../context/useDarkMode'
1010
import UsersContext from '../../context/accountsContext'
1111
import ElevatedView from '../../components/ElevatedView'
12-
import { invalidate } from '../../minecraft/authentication/yggdrasil'
12+
import { invalidate } from '../../minecraft/api/yggdrasil'
1313

1414
// LOW-TODO: Reload to update account info for online mode using /refresh.
1515
// Also, to reload all the skin images?

src/screens/accounts/AddAccountDialog.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Dialog, { dialogStyles } from '../../components/Dialog'
88
import TextField from '../../components/TextField'
99
import useDarkMode from '../../context/useDarkMode'
1010
import UsersContext from '../../context/accountsContext'
11-
import { authenticate } from '../../minecraft/authentication/yggdrasil'
11+
import { authenticate } from '../../minecraft/api/yggdrasil'
1212

1313
const AddAccountDialog = ({
1414
open,

src/screens/accounts/MicrosoftLogin.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
getXstsTokenAndUserHash,
1414
authenticateWithXsts,
1515
getGameProfile
16-
} from '../../minecraft/authentication/microsoft'
16+
} from '../../minecraft/api/microsoft'
1717

1818
const MicrosoftLogin = ({ close }: { close: () => void }) => {
1919
const darkMode = useDarkMode()

yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -2517,6 +2517,11 @@
25172517
dependencies:
25182518
"tweetnacl" "^0.14.3"
25192519

2520+
"big-integer@^1.6.51":
2521+
"integrity" "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg=="
2522+
"resolved" "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz"
2523+
"version" "1.6.51"
2524+
25202525
"bl@^4.1.0":
25212526
"integrity" "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="
25222527
"resolved" "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz"

0 commit comments

Comments
 (0)