mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-15 02:29:21 +03:00
118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
package lua
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ed25519"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"sync"
|
|
|
|
"github.com/xtls/xray-core/common/errors"
|
|
)
|
|
|
|
var privilegedKey struct {
|
|
sync.Once
|
|
publicKey ed25519.PublicKey
|
|
}
|
|
|
|
// SetPrivilegedPublicKey configures the trusted Ed25519 public key once at
|
|
// startup, before compiling scripts. An empty value disables authorization.
|
|
// Nonempty values must be unpadded Base64URL encodings of exactly 32 bytes.
|
|
func SetPrivilegedPublicKey(value string) error {
|
|
var publicKey ed25519.PublicKey
|
|
if value != "" {
|
|
decoded, err := decodePrivilegedValue(value, ed25519.PublicKeySize)
|
|
if err != nil {
|
|
return errors.New("invalid Lua privileged public key").Base(err)
|
|
}
|
|
publicKey = ed25519.PublicKey(decoded)
|
|
}
|
|
privilegedKey.Do(func() {
|
|
privilegedKey.publicKey = publicKey
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func decodePrivilegedValue(value string, size int) ([]byte, error) {
|
|
encoding := base64.RawURLEncoding.Strict()
|
|
if len(value) != encoding.EncodedLen(size) {
|
|
return nil, errors.New("expected an unpadded Base64URL encoding of ", size, " bytes")
|
|
}
|
|
decoded, err := encoding.DecodeString(value)
|
|
if err != nil || len(decoded) != size || encoding.EncodeToString(decoded) != value {
|
|
return nil, errors.New("expected an unpadded Base64URL encoding of ", size, " bytes")
|
|
}
|
|
return decoded, nil
|
|
}
|
|
|
|
func verifyPrivilegedSignature(code []byte) (bool, error) {
|
|
_, end, value, found := privilegedSignatureLine(code)
|
|
if !found {
|
|
return false, nil
|
|
}
|
|
|
|
publicKey := privilegedKey.publicKey
|
|
if len(publicKey) == 0 {
|
|
return false, errors.New("Lua privileged signature requires --lua-privileged-pubkey")
|
|
}
|
|
signature, err := decodePrivilegedValue(string(bytes.TrimSpace(value)), ed25519.SignatureSize)
|
|
if err != nil {
|
|
return false, errors.New("invalid Lua privileged signature").Base(err)
|
|
}
|
|
digest := sha256.Sum256(bytes.TrimSpace(code[end:]))
|
|
if !ed25519.Verify(publicKey, digest[:], signature) {
|
|
return false, errors.New("Lua privileged signature verification failed")
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// SignPrivilegedScript signs the script body, then prepends or replaces the signature line.
|
|
func SignPrivilegedScript(code []byte, seed string) ([]byte, error) {
|
|
decoded, err := decodePrivilegedValue(seed, ed25519.SeedSize)
|
|
if err != nil {
|
|
return nil, errors.New("invalid Ed25519 private key seed").Base(err)
|
|
}
|
|
|
|
start, end, _, found := privilegedSignatureLine(code)
|
|
body := code
|
|
if found {
|
|
body = code[end:]
|
|
}
|
|
|
|
digest := sha256.Sum256(bytes.TrimSpace(body))
|
|
signature := ed25519.Sign(ed25519.NewKeyFromSeed(decoded), digest[:])
|
|
header := []byte("-- @xray:privileged-signature=" + base64.RawURLEncoding.EncodeToString(signature))
|
|
|
|
if !found {
|
|
if newline := bytes.IndexByte(code, '\n'); newline > 0 && code[newline-1] == '\r' {
|
|
header = append(header, '\r')
|
|
}
|
|
header = append(header, '\n')
|
|
return append(header, code...), nil
|
|
}
|
|
signed := make([]byte, 0, start+len(header)+len(code)-end)
|
|
signed = append(signed, code[:start]...)
|
|
signed = append(signed, header...)
|
|
return append(signed, code[end:]...), nil
|
|
}
|
|
|
|
func privilegedSignatureLine(code []byte) (start, end int, value []byte, found bool) {
|
|
for rest := code; len(rest) != 0; {
|
|
start = len(code) - len(rest)
|
|
var line []byte
|
|
line, rest, _ = bytes.Cut(rest, []byte{'\n'})
|
|
line = bytes.TrimSuffix(line, []byte{'\r'})
|
|
if len(bytes.TrimSpace(line)) == 0 {
|
|
continue
|
|
}
|
|
end = start + len(line)
|
|
line, found := bytes.CutPrefix(bytes.TrimLeft(line, " \t"), []byte("--"))
|
|
if !found {
|
|
return 0, 0, nil, false
|
|
}
|
|
value, found = bytes.CutPrefix(bytes.TrimLeft(line, " \t"), []byte("@xray:privileged-signature="))
|
|
return start, end, value, found
|
|
}
|
|
return 0, 0, nil, false
|
|
}
|