From 86f392b39750752ed7ea4076b54a04e01b56e070 Mon Sep 17 00:00:00 2001 From: zhann Date: Sun, 29 Mar 2026 21:00:51 +0700 Subject: [PATCH 1/2] feat: custom pairing code --- pair-code.go | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pair-code.go b/pair-code.go index 9774ba00a..16cf71bd1 100644 --- a/pair-code.go +++ b/pair-code.go @@ -54,16 +54,23 @@ type phoneLinkingCache struct { pairingRef string } -func generateCompanionEphemeralKey() (ephemeralKeyPair *keys.KeyPair, ephemeralKey []byte, encodedLinkingCode string) { +func generateCompanionEphemeralKey(pairingCode string) (ephemeralKeyPair *keys.KeyPair, ephemeralKey []byte, encodedLinkingCode string) { ephemeralKeyPair = keys.NewKeyPair() salt := random.Bytes(32) iv := random.Bytes(16) - linkingCode := random.Bytes(5) - encodedLinkingCode = linkingBase32.EncodeToString(linkingCode) + + if != "" && len(pairingCode) == 8 { + encodedLinkingCode = pairingCode + } else { + linkingCode := random.Bytes(5) + encodedLinkingCode = linkingBase32.EncodeToString(linkingCode) + } + linkCodeKey := pbkdf2.Key([]byte(encodedLinkingCode), salt, 2<<16, 32, sha256.New) linkCipherBlock, _ := aes.NewCipher(linkCodeKey) encryptedPubkey := ephemeralKeyPair.Pub[:] cipher.NewCTR(linkCipherBlock, iv).XORKeyStream(encryptedPubkey, encryptedPubkey) + ephemeralKey = make([]byte, 80) copy(ephemeralKey[0:32], salt) copy(ephemeralKey[32:48], iv) @@ -86,12 +93,22 @@ func generateCompanionEphemeralKey() (ephemeralKeyPair *keys.KeyPair, ephemeralK // The client display name must be formatted as `Browser (OS)`, and only common browsers/OSes are allowed // (the server will validate it and return 400 if it's wrong). // +// The optional pairingCode parameter allows specifying a custom 8-character pairing code using valid base32 characters. +// If not provided or invalid, a random code will be generated. +// // See https://faq.whatsapp.com/1324084875126592 for more info -func (cli *Client) PairPhone(ctx context.Context, phone string, showPushNotification bool, clientType PairClientType, clientDisplayName string) (string, error) { +func (cli *Client) PairPhone(ctx context.Context, phone string, showPushNotification bool, clientType PairClientType, clientDisplayName string, pairingCode ...string) (string, error) { if cli == nil { return "", ErrClientIsNil } - ephemeralKeyPair, ephemeralKey, encodedLinkingCode := generateCompanionEphemeralKey() + + var pairCode string + if len(pairingCode) > 0 && pairingCode[0] != "" { + pairCode = pairingCode[0] + } + + ephemeralKeyPair, ephemeralKey, encodedLinkingCode := generateCompanionEphemeralKey(pairCode) + phone = notNumbers.ReplaceAllString(phone, "") if len(phone) <= 6 { return "", ErrPhoneNumberTooShort @@ -182,7 +199,7 @@ func (cli *Client) handleCodePairNotification(ctx context.Context, parentNode *w keyBundleSalt := random.Bytes(32) keyBundleNonce := random.Bytes(12) - // Decrypt the primary device's ephemeral public key, which was encrypted with the 8-character pairing code, + // Decrypt the primary device's ephemeral public key, which was encrypted with the pairing code, // then compute the DH shared secret using our ephemeral private key we generated earlier. primarySalt := wrappedPrimaryEphemeralPub[0:32] primaryIV := wrappedPrimaryEphemeralPub[32:48] From e364489d91db1f9c22e0f742365f423f062c9aeb Mon Sep 17 00:00:00 2001 From: zhann Date: Mon, 30 Mar 2026 05:36:31 +0700 Subject: [PATCH 2/2] fix: syntax error --- pair-code.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pair-code.go b/pair-code.go index 16cf71bd1..1e652e17a 100644 --- a/pair-code.go +++ b/pair-code.go @@ -59,7 +59,7 @@ func generateCompanionEphemeralKey(pairingCode string) (ephemeralKeyPair *keys.K salt := random.Bytes(32) iv := random.Bytes(16) - if != "" && len(pairingCode) == 8 { + if pairingCode != "" && len(pairingCode) == 8 { encodedLinkingCode = pairingCode } else { linkingCode := random.Bytes(5)