mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-24 20:07:13 +00:00
feat(pia): add PIA login-and-add WireGuard outbounds (#6272)
* feat(pia): add login-and-add WireGuard outbounds (#2) * fix(pia): keep PIA outbounds identifiable after the editor strips hostname The outbound editor drops piaHostname, so last-segment matching failed for hyphenated servers. Identify rows by the computed tag, re-encrypt stored tokens onto the active key, skip unusable catalog rows, and always release the catalog refresh latch.
This commit is contained in:
@@ -97,12 +97,26 @@ func IsEncrypted(stored string) bool { return strings.HasPrefix(stored, encPrefi
|
||||
// Encrypt returns plaintext in ModeOff or row-bound enc:v1 ciphertext otherwise.
|
||||
// Empty and already-valid encrypted values remain unchanged.
|
||||
func (c *Codec) Encrypt(nodeID int, plaintext string) (string, error) {
|
||||
return c.EncryptBound(aad(nodeID), plaintext)
|
||||
}
|
||||
|
||||
// Decrypt passes legacy plaintext through; enc: values must authenticate and
|
||||
// are never reinterpreted as plaintext after an error.
|
||||
func (c *Codec) Decrypt(nodeID int, stored string) (string, error) {
|
||||
pt, err := c.DecryptBound(aad(nodeID), stored)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("nodetoken: node %d: %w", nodeID, err)
|
||||
}
|
||||
return pt, nil
|
||||
}
|
||||
|
||||
// EncryptBound is Encrypt with an explicit AAD (e.g. settings/pia_token).
|
||||
func (c *Codec) EncryptBound(bound []byte, plaintext string) (string, error) {
|
||||
if c.mode == ModeOff || plaintext == "" {
|
||||
return plaintext, nil
|
||||
}
|
||||
if IsEncrypted(plaintext) {
|
||||
// Validate it actually decrypts for this node; if so keep verbatim.
|
||||
if _, err := c.Decrypt(nodeID, plaintext); err != nil {
|
||||
if _, err := c.DecryptBound(bound, plaintext); err != nil {
|
||||
return "", fmt.Errorf("nodetoken: refusing to store undecryptable ciphertext: %w", err)
|
||||
}
|
||||
return plaintext, nil
|
||||
@@ -119,14 +133,13 @@ func (c *Codec) Encrypt(nodeID int, plaintext string) (string, error) {
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return "", err
|
||||
}
|
||||
ct := gcm.Seal(nil, nonce, []byte(plaintext), aad(nodeID))
|
||||
ct := gcm.Seal(nil, nonce, []byte(plaintext), bound)
|
||||
blob := append(nonce, ct...)
|
||||
return encScheme + c.ring.ActiveID + ":" + base64.RawURLEncoding.EncodeToString(blob), nil
|
||||
}
|
||||
|
||||
// Decrypt passes legacy plaintext through; enc: values must authenticate and
|
||||
// are never reinterpreted as plaintext after an error.
|
||||
func (c *Codec) Decrypt(nodeID int, stored string) (string, error) {
|
||||
// DecryptBound is Decrypt with an explicit AAD.
|
||||
func (c *Codec) DecryptBound(bound []byte, stored string) (string, error) {
|
||||
if c.mode == ModeOff {
|
||||
return stored, nil
|
||||
}
|
||||
@@ -159,9 +172,9 @@ func (c *Codec) Decrypt(nodeID int, stored string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pt, err := gcm.Open(nil, blob[:nonceLen], blob[nonceLen:], aad(nodeID))
|
||||
pt, err := gcm.Open(nil, blob[:nonceLen], blob[nonceLen:], bound)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("nodetoken: authentication failed for node %d: %w", nodeID, err)
|
||||
return "", fmt.Errorf("nodetoken: authentication failed: %w", err)
|
||||
}
|
||||
return string(pt), nil
|
||||
}
|
||||
@@ -232,5 +245,12 @@ func get() *Codec {
|
||||
// Encrypt/Decrypt/Enabled operate on the process-wide codec.
|
||||
func Encrypt(nodeID int, plaintext string) (string, error) { return get().Encrypt(nodeID, plaintext) }
|
||||
func Decrypt(nodeID int, stored string) (string, error) { return get().Decrypt(nodeID, stored) }
|
||||
func Enabled() bool { return get().Enabled() }
|
||||
func Active() *Codec { return get() }
|
||||
func EncryptBound(bound []byte, plaintext string) (string, error) {
|
||||
return get().EncryptBound(bound, plaintext)
|
||||
}
|
||||
|
||||
func DecryptBound(bound []byte, stored string) (string, error) {
|
||||
return get().DecryptBound(bound, stored)
|
||||
}
|
||||
func Enabled() bool { return get().Enabled() }
|
||||
func Active() *Codec { return get() }
|
||||
|
||||
@@ -49,6 +49,23 @@ func TestAADBindsToNode(t *testing.T) {
|
||||
// Decrypting under a different node id must fail (ciphertext bound to row).
|
||||
if _, err := c.Decrypt(8, enc); err == nil {
|
||||
t.Fatal("expected AAD mismatch error decrypting under wrong node id")
|
||||
} else if !strings.Contains(err.Error(), "node 8") || !strings.Contains(err.Error(), "authentication failed") {
|
||||
t.Fatalf("wrong-node decrypt error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAADBindsSettingsApartFromNodes(t *testing.T) {
|
||||
c, _ := NewCodec(ModeRequired, testRing(t, "k1", "k1"))
|
||||
enc, err := c.EncryptBound([]byte("settings/pia_token"), "tok")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := c.Decrypt(1, enc); err == nil {
|
||||
t.Fatal("settings/pia_token ciphertext must not decrypt under nodes/api_token/1")
|
||||
}
|
||||
pt, err := c.DecryptBound([]byte("settings/pia_token"), enc)
|
||||
if err != nil || pt != "tok" {
|
||||
t.Fatalf("pia AAD round-trip: %q err=%v", pt, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user