diff --git a/component/generater/types.go b/component/generater/types.go deleted file mode 100644 index 06f59e94..00000000 --- a/component/generater/types.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copy from https://github.com/WireGuard/wgctrl-go/blob/a9ab2273dd1075ea74b88c76f8757f8b4003fcbf/wgtypes/types.go#L71-L155 - -package generater - -import ( - "crypto/rand" - "encoding/base64" - "fmt" - - "golang.org/x/crypto/curve25519" -) - -// KeyLen is the expected key length for a WireGuard key. -const KeyLen = 32 // wgh.KeyLen - -// A Key is a public, private, or pre-shared secret key. The Key constructor -// functions in this package can be used to create Keys suitable for each of -// these applications. -type Key [KeyLen]byte - -// GenerateKey generates a Key suitable for use as a pre-shared secret key from -// a cryptographically safe source. -// -// The output Key should not be used as a private key; use GeneratePrivateKey -// instead. -func GenerateKey() (Key, error) { - b := make([]byte, KeyLen) - if _, err := rand.Read(b); err != nil { - return Key{}, fmt.Errorf("wgtypes: failed to read random bytes: %v", err) - } - - return NewKey(b) -} - -// GeneratePrivateKey generates a Key suitable for use as a private key from a -// cryptographically safe source. -func GeneratePrivateKey() (Key, error) { - key, err := GenerateKey() - if err != nil { - return Key{}, err - } - - // Modify random bytes using algorithm described at: - // https://cr.yp.to/ecdh.html. - key[0] &= 248 - key[31] &= 127 - key[31] |= 64 - - return key, nil -} - -// NewKey creates a Key from an existing byte slice. The byte slice must be -// exactly 32 bytes in length. -func NewKey(b []byte) (Key, error) { - if len(b) != KeyLen { - return Key{}, fmt.Errorf("wgtypes: incorrect key size: %d", len(b)) - } - - var k Key - copy(k[:], b) - - return k, nil -} - -// ParseKey parses a Key from a base64-encoded string, as produced by the -// Key.String method. -func ParseKey(s string) (Key, error) { - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return Key{}, fmt.Errorf("wgtypes: failed to parse base64-encoded key: %v", err) - } - - return NewKey(b) -} - -// PublicKey computes a public key from the private key k. -// -// PublicKey should only be called when k is a private key. -func (k Key) PublicKey() Key { - var ( - pub [KeyLen]byte - priv = [KeyLen]byte(k) - ) - - // ScalarBaseMult uses the correct base value per https://cr.yp.to/ecdh.html, - // so no need to specify it. - curve25519.ScalarBaseMult(&pub, &priv) - - return Key(pub) -} - -// String returns the base64-encoded string representation of a Key. -// -// ParseKey can be used to produce a new Key from this string. -func (k Key) String() string { - return base64.StdEncoding.EncodeToString(k[:]) -} diff --git a/component/generater/cmd.go b/component/generator/cmd.go similarity index 82% rename from component/generater/cmd.go rename to component/generator/cmd.go index fdf14855..537fba91 100644 --- a/component/generater/cmd.go +++ b/component/generator/cmd.go @@ -1,4 +1,4 @@ -package generater +package generator import ( "encoding/base64" @@ -22,20 +22,19 @@ func Main(args []string) { } fmt.Println(newUUID.String()) case "reality-keypair": - privateKey, err := GeneratePrivateKey() + privateKey, err := GenX25519PrivateKey() if err != nil { panic(err) } - publicKey := privateKey.PublicKey() - fmt.Println("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey[:])) - fmt.Println("PublicKey: " + base64.RawURLEncoding.EncodeToString(publicKey[:])) + fmt.Println("PrivateKey: " + base64.RawURLEncoding.EncodeToString(privateKey.Bytes())) + fmt.Println("PublicKey: " + base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes())) case "wg-keypair": - privateKey, err := GeneratePrivateKey() + privateKey, err := GenX25519PrivateKey() if err != nil { panic(err) } - fmt.Println("PrivateKey: " + privateKey.String()) - fmt.Println("PublicKey: " + privateKey.PublicKey().String()) + fmt.Println("PrivateKey: " + base64.StdEncoding.EncodeToString(privateKey.Bytes())) + fmt.Println("PublicKey: " + base64.StdEncoding.EncodeToString(privateKey.PublicKey().Bytes())) case "ech-keypair": if len(args) < 2 { panic("Using: generate ech-keypair ") diff --git a/component/generator/x25519.go b/component/generator/x25519.go new file mode 100644 index 00000000..e99fff2b --- /dev/null +++ b/component/generator/x25519.go @@ -0,0 +1,27 @@ +package generator + +import ( + "crypto/ecdh" + "crypto/rand" +) + +const X25519KeySize = 32 + +func GenX25519PrivateKey() (*ecdh.PrivateKey, error) { + var privateKey [X25519KeySize]byte + _, err := rand.Read(privateKey[:]) + if err != nil { + return nil, err + } + + // Avoid generating equivalent X25519 private keys + // https://github.com/XTLS/Xray-core/pull/1747 + // + // Modify random bytes using algorithm described at: + // https://cr.yp.to/ecdh.html. + privateKey[0] &= 248 + privateKey[31] &= 127 + privateKey[31] |= 64 + + return ecdh.X25519().NewPrivateKey(privateKey[:]) +} diff --git a/listener/inbound/common_test.go b/listener/inbound/common_test.go index 5b838bd3..75b98e0b 100644 --- a/listener/inbound/common_test.go +++ b/listener/inbound/common_test.go @@ -21,7 +21,7 @@ import ( "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/ech" - "github.com/metacubex/mihomo/component/generater" + "github.com/metacubex/mihomo/component/generator" tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" @@ -48,13 +48,12 @@ var echConfigBase64, echKeyPem, _ = ech.GenECHConfig(echPublicSni) func init() { rand.Read(httpData) - privateKey, err := generater.GeneratePrivateKey() + privateKey, err := generator.GenX25519PrivateKey() if err != nil { panic(err) } - publicKey := privateKey.PublicKey() - realityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey[:]) - realityPublickey = base64.RawURLEncoding.EncodeToString(publicKey[:]) + realityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey.Bytes()) + realityPublickey = base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes()) } type TestTunnel struct { diff --git a/main.go b/main.go index 3bc3d74f..6a85e5df 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "strings" "syscall" - "github.com/metacubex/mihomo/component/generater" + "github.com/metacubex/mihomo/component/generator" "github.com/metacubex/mihomo/component/geodata" "github.com/metacubex/mihomo/component/updater" "github.com/metacubex/mihomo/config" @@ -73,7 +73,7 @@ func main() { } if len(os.Args) > 1 && os.Args[1] == "generate" { - generater.Main(os.Args[2:]) + generator.Main(os.Args[2:]) return } diff --git a/transport/vless/encryption/key.go b/transport/vless/encryption/key.go index c1cab173..03ff284e 100644 --- a/transport/vless/encryption/key.go +++ b/transport/vless/encryption/key.go @@ -56,6 +56,9 @@ func GenX25519(privateKeyStr string) (privateKeyBase64, passwordBase64 string, e } } + // Avoid generating equivalent X25519 private keys + // https://github.com/XTLS/Xray-core/pull/1747 + // // Modify random bytes using algorithm described at: // https://cr.yp.to/ecdh.html. privateKey[0] &= 248