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

  1. Generate ServerRandom with the same helper the library uses: 32 bytes from crypto/rand (see newCryptoRandom / CryptoRandomSize).
  2. Ensure the []byte field survives JSON round-trips as base64 — do not convert it to a hex string manually.
  3. 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

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


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/fd04ccfc315261e6. Report an issue: GitHub.