fatedier/frp · error
invalid crypto server random length %d, want %d
Error message
invalid crypto server random length %d, want %d
What it means
Thrown by ValidateServerHelloForClient when ServerHello.Selected.Crypto.ServerRandom is not exactly CryptoRandomSize (32) bytes. The server random feeds key derivation, so a wrong length indicates a broken or truncated handshake message.
Source
Thrown at pkg/proto/wire/crypto.go:113
}
udpPacketCodec := serverHello.Selected.Message.UDPPacketCodec
if udpPacketCodec != "" {
if udpPacketCodec != UDPPacketCodecBinary {
return fmt.Errorf("unsupported selected UDP packet codec: %s", udpPacketCodec)
}
if !Supports(clientHello.Capabilities.Message.UDPPacketCodecs, udpPacketCodec) {
return fmt.Errorf("selected UDP packet codec was not advertised by client: %s", udpPacketCodec)
}
}
cryptoSelection := serverHello.Selected.Crypto
if !IsSupportedAEADAlgorithm(cryptoSelection.Algorithm) {
return fmt.Errorf("unknown selected crypto algorithm: %s", cryptoSelection.Algorithm)
}
if !Supports(clientHello.Capabilities.Crypto.Algorithms, cryptoSelection.Algorithm) {
return fmt.Errorf("selected crypto algorithm was not advertised by client: %s", cryptoSelection.Algorithm)
}
if len(cryptoSelection.ServerRandom) != CryptoRandomSize {
return fmt.Errorf("invalid crypto server random length %d, want %d", len(cryptoSelection.ServerRandom), CryptoRandomSize)
}
return nil
}
func selectUDPPacketCodec(codecs []string) string {
if Supports(codecs, UDPPacketCodecBinary) {
return UDPPacketCodecBinary
}
return ""
}
func NewCryptoContext(algorithm string, clientHelloPayload, serverHelloPayload []byte) *CryptoContext {
return &CryptoContext{
Algorithm: algorithm,
TranscriptHash: HashCryptoTranscript(clientHelloPayload, serverHelloPayload),
}
}
View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Generate ServerRandom with the same helper the library uses: 32 bytes from crypto/rand (see newCryptoRandom / CryptoRandomSize).
- Ensure the []byte field survives JSON round-trips as base64 — do not convert it to a hex string manually.
- Check for payload truncation if the error appears only in production (MTU/proxy issues mangling the hello frame).
Example fix
// before
selected.Crypto.ServerRandom = make([]byte, 16)
// after
random, err := newCryptoRandom() // 32 bytes, crypto/rand
if err != nil { return err }
selected.Crypto.ServerRandom = random Defensive patterns
Strategy: validation
Validate before calling
if got := len(serverHello.Selected.Crypto.ServerRandom); got != wire.CryptoRandomSize {
return fmt.Errorf("server random is %d bytes, want %d — truncated or mis-encoded hello", got, wire.CryptoRandomSize)
} Prevention
- Generate randoms with crypto/rand at exactly wire.CryptoRandomSize bytes.
- Never hand-encode []byte random fields as hex strings; JSON base64 of the raw bytes is the only correct form.
When it happens
Trigger: Server sends Selected.Crypto.ServerRandom that is nil, empty, or any length other than 32 bytes (e.g. 16-byte random, base64 string put in the field raw instead of decoded, or hex-encoded 64 bytes).
Common situations: Test mocks that fill the field with random data of arbitrary size; a JSON encoding path where the []byte is marshaled as base64 and then re-marshaled as a string on the other side, doubling length; older servers that used a different random size.
Related errors
- invalid crypto client random length %d, want %d
- no supported crypto algorithm
- unknown selected crypto algorithm: %s
- selected crypto algorithm was not advertised by client: %s
- decode ClientHello transcript: %w
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/fd04ccfc315261e6.
Report an issue: GitHub.