{"record":{"id":"ac2c3c473dd536db","repo":"XTLS/Xray-core","slug":"psk-must-be-at-least-d-bytes","errorCode":null,"errorMessage":"PSK must be at least %d bytes","messagePattern":"PSK must be at least (.+?) bytes","errorType":"validation","errorClass":"ErrPSKTooShort","httpStatus":null,"severity":"error","filePath":"transport/internet/finalmask/salamander/salamander.go","lineNumber":18,"sourceCode":"package salamander\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"sync\"\n\n\t\"github.com/xtls/xray-core/common\"\n\t\"golang.org/x/crypto/blake2b\"\n)\n\nconst (\n\tsmPSKMinLen = 4\n\tsmSaltLen   = 8\n\tsmKeyLen    = blake2b.Size256\n)\n\nvar ErrPSKTooShort = fmt.Errorf(\"PSK must be at least %d bytes\", smPSKMinLen)\n\n// SalamanderObfuscator is an obfuscator that obfuscates each packet with\n// the BLAKE2b-256 hash of a pre-shared key combined with a random salt.\n// Packet format: [8-byte salt][payload]\ntype SalamanderObfuscator struct {\n\tPSK []byte\n\n\tlk       sync.Mutex\n\tkeyInput []byte\n}\n\nfunc NewSalamanderObfuscator(psk []byte) (*SalamanderObfuscator, error) {\n\tif len(psk) < smPSKMinLen {\n\t\treturn nil, ErrPSKTooShort\n\t}\n\tpskCopy := append([]byte(nil), psk...)\n\tkeyInput := make([]byte, len(pskCopy)+smSaltLen)\n\tcopy(keyInput, pskCopy)","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/transport/internet/finalmask/salamander/salamander.go#L1-L36","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","solutions":["Use a PSK of at least 4 bytes — in practice a long random secret (16-32 bytes)","If the key is encoded, confirm the decode path produces the intended byte length before passing it in","Keep the PSK identical on both peers; only its length/format matter for this check","Compare errors.Is(err, salamander.ErrPSKTooShort) to give a precise config error message"],"exampleFix":"// before\nob, err := salamander.NewSalamanderObfuscator([]byte(\"ab\"))\n\n// after\nob, err := salamander.NewSalamanderObfuscator([]byte(\"at-least-4-bytes\"))","handlingStrategy":"validation","validationCode":"import \"errors\"\n\nfunc validSalamanderPSK(psk []byte) error {\n    if len(psk) < 4 {\n        return errors.New(\"salamander PSK too short: need >= 4 bytes\")\n    }\n    return nil\n}\n// call before salamander.NewSalamanderObfuscator(psk)","typeGuard":null,"tryCatchPattern":"ob, err := salamander.NewSalamanderObfuscator(psk)\nif err != nil {\n    if errors.Is(err, salamander.ErrPSKTooShort) {\n        return fmt.Errorf(\"config error: PSK must be >= 4 bytes, got %d\", len(psk))\n    }\n    return err\n}","preventionTips":["Use 16-32 byte random PSKs in practice","Verify encoded-key decode length before passing bytes in","Compare with errors.Is against the exported ErrPSKTooShort sentinel"],"tags":["salamander","obfuscation","psk","validation"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}