fatedier/frp · error

unknown selected crypto algorithm: %s

Error message

unknown selected crypto algorithm: %s

What it means

Thrown by ValidateServerHelloForClient when the crypto algorithm in ServerHello.Selected.Crypto.Algorithm is not in supportedAEADAlgorithms ("aes-256-gcm" or "xchacha20-poly1305"). This catches servers that select an algorithm this build cannot instantiate, before any AEAD cipher is constructed.

Source

Thrown at pkg/proto/wire/crypto.go:107

	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)
		}
	}
	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 {

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Run the same frp version on client and server so the supported algorithm sets are identical.
  2. On the server, select the algorithm only via SelectAEADAlgorithm(clientHello...Algorithms), never from config strings or user input.
  3. Verify the ServerHello payload is not being truncated or modified in transit (TLS/proxy setups that mangle bodies).

Example fix

// before (server, hand-rolled)
selected.Crypto.Algorithm = "aes-128-gcm"

// after
algorithm, ok := wire.SelectAEADAlgorithm(clientHello.Capabilities.Crypto.Algorithms)
if !ok { return errors.New("no shared algorithm") }
selected.Crypto.Algorithm = algorithm
Defensive patterns

Strategy: validation

Validate before calling

if !wire.IsSupportedAEADAlgorithm(serverHello.Selected.Crypto.Algorithm) {
    return fmt.Errorf("server selected unknown algorithm %q — likely version mismatch",
        serverHello.Selected.Crypto.Algorithm)
}

Prevention

When it happens

Trigger: Server sends an algorithm string outside the supported set — empty string, "aes-128-gcm", typo, or an algorithm introduced in a newer frp version. Detected in NewClientCryptoContext after decoding the ServerHello transcript.

Common situations: Version mismatch between client and server; corrupted handshake bytes that still parse as JSON; malicious server attempting to downgrade to a weak/unknown cipher.

Related errors


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