XTLS/Xray-core · error
new crypto writer: %w
Error message
new crypto writer: %w
What it means
Constructing the encrypting stream cipher (newCryptoWriter) over the connection writer with the negotiated shared secret failed. Symmetric with the crypto reader: it initializes AES/CFB8 in encrypt direction with the same 16-byte shared secret. Because the reader was already constructed with the identical key one line earlier, a failure here indicates a writer-side wrapping problem or code drift, not a bad secret.
Source
Thrown at transport/internet/finalmask/xmc/server.go:213
}
decryptedVerifyToken, err = rsa.DecryptPKCS1v15(rand.Reader, c.rsaPrivateKey, encryptedVerifyToken)
if err != nil {
return fmt.Errorf("decrypt verify token: %w", err)
}
if len(decryptedVerifyToken) < 4 || !bytes.Equal(verifyToken, decryptedVerifyToken[:4]) {
return fmt.Errorf("verify token mismatch")
}
c.reader, err = newCryptoReader(c.reader, sharedSecret)
if err != nil {
return fmt.Errorf("new crypto reader: %w", err)
}
c.writer, err = newCryptoWriter(c.writer, sharedSecret)
if err != nil {
return fmt.Errorf("new crypto writer: %w", err)
}
// verify password
receivedPassword := decryptedVerifyToken[4:]
if subtle.ConstantTimeCompare(receivedPassword, []byte(c.password)) != 1 {
writeDisconnectPacket(c.writer, `{"type":"translatable","translate":"multiplayer.disconnect.authservers_down"}`)
return fmt.Errorf("bad password")
}
if !found {
if err = writeDisconnectPacket(c.writer, `{"text":"You are not white-listed on this server!"}`); err != nil {
return fmt.Errorf("write unknown login profile disconnect: %w", err)
}
return fmt.Errorf("unknown login profile")
}
loginName := String(profile.Username)
propertyCount := Varint(1)View on GitHub (pinned to 7d214f8b09)
Solutions
- Diff newCryptoWriter against newCryptoReader — the key path should be identical; any asymmetry is the bug.
- Unit-test both constructors with the same random 16-byte key.
- On failure, tear down the whole connection (it is half-encrypted and unusable).
Defensive patterns
Strategy: validation
Validate before calling
if len(sharedSecret) != 16 {
return fmt.Errorf("bad shared secret length: %d", len(sharedSecret))
} Try / catch
if _, err := newCryptoWriter(c.writer, sharedSecret); err != nil {
// reader already succeeded with this key; writer failure is a bug — tear down the connection
return fmt.Errorf("new crypto writer: %w", err)
} Prevention
- Construct reader and writer back-to-back with the same key, as the current code does.
- Share one cipher-construction helper between reader and writer so they cannot drift.
- On any crypto-layer construction failure, close the connection fully — half-encrypted sessions are unusable.
When it happens
Trigger: newCryptoWriter's internal aes.NewCipher or writer-wrapping returns an error; only reachable through code drift since the same key just succeeded in newCryptoReader.
Common situations: Custom transports replacing c.writer with something that errors when wrapped; refactors that changed newCryptoWriter's key handling independently of the reader.
Related errors
- new crypto reader: %w
- new crypto reader: %w
- new crypto writer: %w
- bad shared secret length: %d
- shadowsocks 2022 (multi-user): only blake3-aes-*-gcm methods
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/f9f391232728b0e1.
Report an issue: GitHub.