fatedier/frp · error
invalid crypto client random length %d, want %d
Error message
invalid crypto client random length %d, want %d
What it means
ValidateCryptoCapabilities rejected a ClientHello because its ClientRandom is not exactly CryptoRandomSize (32) bytes. The 32-byte random is a hard protocol requirement — it feeds the transcript hash used to derive session keys, so a wrong length breaks key derivation.
Source
Thrown at pkg/proto/wire/crypto.go:84
return ServerHello{}, err
}
return ServerHello{
Selected: ServerSelection{
Message: MessageSelection{
Codec: MessageCodecJSON,
UDPPacketCodec: selectUDPPacketCodec(clientHello.Capabilities.Message.UDPPacketCodecs),
},
Crypto: CryptoSelection{
Algorithm: algorithm,
ServerRandom: serverRandom,
},
},
}, nil
}
func ValidateCryptoCapabilities(c CryptoCapabilities) error {
if len(c.ClientRandom) != CryptoRandomSize {
return fmt.Errorf("invalid crypto client random length %d, want %d", len(c.ClientRandom), CryptoRandomSize)
}
if _, ok := SelectAEADAlgorithm(c.Algorithms); !ok {
return fmt.Errorf("no supported crypto algorithm")
}
return nil
}
func ValidateServerHelloForClient(clientHello ClientHello, serverHello ServerHello) error {
if serverHello.Selected.Message.Codec != MessageCodecJSON {
return fmt.Errorf("unsupported selected message codec: %s", serverHello.Selected.Message.Codec)
}
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)View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Always create client random via NewClientHello / newCryptoRandom, which guarantees 32 bytes from crypto/rand
- If building hello manually, copy exactly 32 bytes: make([]byte, wire.CryptoRandomSize) filled by crypto/rand
- Align peer versions so the wire format matches
Example fix
// before
hello := clientHelloWithCryptoRandom(bootstrap, []byte("short")) // len 5
// after
r := make([]byte, wire.CryptoRandomSize) // 32
rand.Read(r)
hello := clientHelloWithCryptoRandom(bootstrap, r) Defensive patterns
Strategy: validation
Validate before calling
if len(hello.Capabilities.Crypto.ClientRandom) != wire.CryptoRandomSize {
return fmt.Errorf("client random must be %d bytes", wire.CryptoRandomSize)
} Type guard
func hasValidClientRandom(c wire.CryptoCapabilities) bool {
return len(c.ClientRandom) == wire.CryptoRandomSize
} Try / catch
if err := wire.ValidateClientHello(hello); err != nil {
return fmt.Errorf("dropping malformed hello: %w", err)
} Prevention
- Never hand-build ClientRandom; use NewClientHello so random generation is correct
- Validate deserialized hellos before acting on them
When it happens
Trigger: Constructing or deserializing a ClientHello (or calling ValidateClientHello/ValidateCryptoCapabilities) where Capabilities.Crypto.ClientRandom has len != 32 — hand-built hello, truncated field, or a peer using a different protocol revision.
Common situations: Custom test code filling ClientRandom with a short constant; version skew changing the field's encoding; deserialization bug dropping bytes.
Related errors
- no supported crypto algorithm
- invalid crypto server random length %d, want %d
- 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/e41df29bfc79418d.
Report an issue: GitHub.