fatedier/frp · error
selected crypto algorithm was not advertised by client: %s
Error message
selected crypto algorithm was not advertised by client: %s
What it means
Thrown by ValidateServerHelloForClient when the server selects a crypto algorithm that, while known, was never offered by this client in ClientHello.Capabilities.Crypto.Algorithms. It is an anti-downgrade / anti-surprise guard: the server must pick from what the client advertised.
Source
Thrown at pkg/proto/wire/crypto.go:110
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)
}
}
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
- On the server, always select through SelectAEADAlgorithm(clientHello.Capabilities.Crypto.Algorithms) which iterates the client's own list.
- On the client, send the full PreferredAEADAlgorithms() list so any reasonable server choice is covered.
- Regenerate paired hello fixtures from a single handshake helper in tests.
Example fix
// before (client, restrictive)
hello.Capabilities.Crypto.Algorithms = []string{"xchacha20-poly1305"}
// after
hello.Capabilities.Crypto.Algorithms = wire.PreferredAEADAlgorithms() Defensive patterns
Strategy: validation
Validate before calling
offered := clientHello.Capabilities.Crypto.Algorithms
selected := serverHello.Selected.Crypto.Algorithm
if !wire.Supports(offered, selected) {
return fmt.Errorf("server selected %q which client never offered", selected)
} Prevention
- Clients: send the full PreferredAEADAlgorithms() list so any server preference is valid.
- Servers: never let local config override client-advertised capabilities.
When it happens
Trigger: Client advertises only ["xchacha20-poly1305"] but the server responds with Selected.Crypto.Algorithm = "aes-256-gcm" — the algorithm is valid but Supports(clientHello.Capabilities.Crypto.Algorithms, ...) is false.
Common situations: Server-side selection logic that ignores client capabilities (e.g. always preferring aes-256-gcm on hardware with AES-NI); test fixtures where client and server hellos are constructed separately; a tampering middlebox changing the algorithm field.
Related errors
- no supported crypto algorithm
- invalid crypto client random length %d, want %d
- selected UDP packet codec was not advertised by client: %s
- unknown selected crypto algorithm: %s
- invalid crypto server random length %d, want %d
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/d49e23fa6233a841.
Report an issue: GitHub.