XTLS/Xray-core · error · ErrPSKTooShort

PSK must be at least %d bytes

Error message

PSK must be at least %d bytes

What it means

NewSalamanderObfuscator (finalmask/salamander salting obfuscator) requires the pre-shared key to be at least smPSKMinLen = 4 bytes; shorter keys are rejected via the sentinel ErrPSKTooShort before any obfuscation state is built. The PSK seeds a BLAKE2b-256 key combined with a per-packet 8-byte random salt.

Source

Thrown at transport/internet/finalmask/salamander/salamander.go:18

package salamander

import (
	"crypto/rand"
	"fmt"
	"sync"

	"github.com/xtls/xray-core/common"
	"golang.org/x/crypto/blake2b"
)

const (
	smPSKMinLen = 4
	smSaltLen   = 8
	smKeyLen    = blake2b.Size256
)

var ErrPSKTooShort = fmt.Errorf("PSK must be at least %d bytes", smPSKMinLen)

// SalamanderObfuscator is an obfuscator that obfuscates each packet with
// the BLAKE2b-256 hash of a pre-shared key combined with a random salt.
// Packet format: [8-byte salt][payload]
type SalamanderObfuscator struct {
	PSK []byte

	lk       sync.Mutex
	keyInput []byte
}

func NewSalamanderObfuscator(psk []byte) (*SalamanderObfuscator, error) {
	if len(psk) < smPSKMinLen {
		return nil, ErrPSKTooShort
	}
	pskCopy := append([]byte(nil), psk...)
	keyInput := make([]byte, len(pskCopy)+smSaltLen)
	copy(keyInput, pskCopy)

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Use a PSK of at least 4 bytes — in practice a long random secret (16-32 bytes)
  2. If the key is encoded, confirm the decode path produces the intended byte length before passing it in
  3. Keep the PSK identical on both peers; only its length/format matter for this check
  4. Compare errors.Is(err, salamander.ErrPSKTooShort) to give a precise config error message

Example fix

// before
ob, err := salamander.NewSalamanderObfuscator([]byte("ab"))

// after
ob, err := salamander.NewSalamanderObfuscator([]byte("at-least-4-bytes"))
Defensive patterns

Strategy: validation

Validate before calling

import "errors"

func validSalamanderPSK(psk []byte) error {
    if len(psk) < 4 {
        return errors.New("salamander PSK too short: need >= 4 bytes")
    }
    return nil
}
// call before salamander.NewSalamanderObfuscator(psk)

Try / catch

ob, err := salamander.NewSalamanderObfuscator(psk)
if err != nil {
    if errors.Is(err, salamander.ErrPSKTooShort) {
        return fmt.Errorf("config error: PSK must be >= 4 bytes, got %d", len(psk))
    }
    return err
}

Prevention

When it happens

Trigger: Constructing a SalamanderObfuscator with a psk shorter than 4 bytes — e.g. a 1-3 character string converted to bytes, or a decoded key that came out empty/short (bad base64, wrong codec).

Common situations: Configuring the salamander obfuscation layer with a trivially short password; base64/hex decode of the configured key producing fewer bytes than expected due to encoding mismatches (padded vs raw, hex vs base64).

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/ac2c3c473dd536db. Report an issue: GitHub.