Add dependencies/vendor (whatsapp)
This commit is contained in:
235
vendor/go.mau.fi/whatsmeow/prekeys.go
vendored
Normal file
235
vendor/go.mau.fi/whatsmeow/prekeys.go
vendored
Normal file
@@ -0,0 +1,235 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/keys/prekey"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/util/keys"
|
||||
)
|
||||
|
||||
const (
|
||||
// WantedPreKeyCount is the number of prekeys that the client should upload to the WhatsApp servers in a single batch.
|
||||
WantedPreKeyCount = 50
|
||||
// MinPreKeyCount is the number of prekeys when the client will upload a new batch of prekeys to the WhatsApp servers.
|
||||
MinPreKeyCount = 5
|
||||
)
|
||||
|
||||
func (cli *Client) getServerPreKeyCount() (int, error) {
|
||||
resp, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "encrypt",
|
||||
Type: "get",
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{
|
||||
{Tag: "count"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get prekey count on server: %w", err)
|
||||
}
|
||||
count := resp.GetChildByTag("count")
|
||||
ag := count.AttrGetter()
|
||||
val := ag.Int("value")
|
||||
return val, ag.Error()
|
||||
}
|
||||
|
||||
func (cli *Client) uploadPreKeys() {
|
||||
cli.uploadPreKeysLock.Lock()
|
||||
defer cli.uploadPreKeysLock.Unlock()
|
||||
if cli.lastPreKeyUpload.Add(10 * time.Minute).After(time.Now()) {
|
||||
sc, _ := cli.getServerPreKeyCount()
|
||||
if sc >= WantedPreKeyCount {
|
||||
cli.Log.Debugf("Canceling prekey upload request due to likely race condition")
|
||||
return
|
||||
}
|
||||
}
|
||||
var registrationIDBytes [4]byte
|
||||
binary.BigEndian.PutUint32(registrationIDBytes[:], cli.Store.RegistrationID)
|
||||
preKeys, err := cli.Store.PreKeys.GetOrGenPreKeys(WantedPreKeyCount)
|
||||
if err != nil {
|
||||
cli.Log.Errorf("Failed to get prekeys to upload: %v", err)
|
||||
return
|
||||
}
|
||||
cli.Log.Infof("Uploading %d new prekeys to server", len(preKeys))
|
||||
_, err = cli.sendIQ(infoQuery{
|
||||
Namespace: "encrypt",
|
||||
Type: "set",
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{
|
||||
{Tag: "registration", Content: registrationIDBytes[:]},
|
||||
{Tag: "type", Content: []byte{ecc.DjbType}},
|
||||
{Tag: "identity", Content: cli.Store.IdentityKey.Pub[:]},
|
||||
{Tag: "list", Content: preKeysToNodes(preKeys)},
|
||||
preKeyToNode(cli.Store.SignedPreKey),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
cli.Log.Errorf("Failed to send request to upload prekeys: %v", err)
|
||||
return
|
||||
}
|
||||
cli.Log.Debugf("Got response to uploading prekeys")
|
||||
err = cli.Store.PreKeys.MarkPreKeysAsUploaded(preKeys[len(preKeys)-1].KeyID)
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to mark prekeys as uploaded: %v", err)
|
||||
}
|
||||
cli.lastPreKeyUpload = time.Now()
|
||||
}
|
||||
|
||||
type preKeyResp struct {
|
||||
bundle *prekey.Bundle
|
||||
err error
|
||||
}
|
||||
|
||||
func (cli *Client) fetchPreKeys(users []types.JID) (map[types.JID]preKeyResp, error) {
|
||||
requests := make([]waBinary.Node, len(users))
|
||||
for i, user := range users {
|
||||
requests[i].Tag = "user"
|
||||
requests[i].Attrs = waBinary.Attrs{
|
||||
"jid": user,
|
||||
"reason": "identity",
|
||||
}
|
||||
}
|
||||
resp, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "encrypt",
|
||||
Type: "get",
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "key",
|
||||
Content: requests,
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send prekey request: %w", err)
|
||||
} else if len(resp.GetChildren()) == 0 {
|
||||
return nil, fmt.Errorf("got empty response to prekey request")
|
||||
}
|
||||
list := resp.GetChildByTag("list")
|
||||
respData := make(map[types.JID]preKeyResp)
|
||||
for _, child := range list.GetChildren() {
|
||||
if child.Tag != "user" {
|
||||
continue
|
||||
}
|
||||
jid := child.AttrGetter().JID("jid")
|
||||
jid.AD = true
|
||||
bundle, err := nodeToPreKeyBundle(uint32(jid.Device), child)
|
||||
respData[jid] = preKeyResp{bundle, err}
|
||||
}
|
||||
return respData, nil
|
||||
}
|
||||
|
||||
func preKeyToNode(key *keys.PreKey) waBinary.Node {
|
||||
var keyID [4]byte
|
||||
binary.BigEndian.PutUint32(keyID[:], key.KeyID)
|
||||
node := waBinary.Node{
|
||||
Tag: "key",
|
||||
Content: []waBinary.Node{
|
||||
{Tag: "id", Content: keyID[1:]},
|
||||
{Tag: "value", Content: key.Pub[:]},
|
||||
},
|
||||
}
|
||||
if key.Signature != nil {
|
||||
node.Tag = "skey"
|
||||
node.Content = append(node.GetChildren(), waBinary.Node{
|
||||
Tag: "signature",
|
||||
Content: key.Signature[:],
|
||||
})
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func nodeToPreKeyBundle(deviceID uint32, node waBinary.Node) (*prekey.Bundle, error) {
|
||||
errorNode, ok := node.GetOptionalChildByTag("error")
|
||||
if ok && errorNode.Tag == "error" {
|
||||
return nil, fmt.Errorf("got error getting prekeys: %s", errorNode.XMLString())
|
||||
}
|
||||
|
||||
registrationBytes, ok := node.GetChildByTag("registration").Content.([]byte)
|
||||
if !ok || len(registrationBytes) != 4 {
|
||||
return nil, fmt.Errorf("invalid registration ID in prekey response")
|
||||
}
|
||||
registrationID := binary.BigEndian.Uint32(registrationBytes)
|
||||
|
||||
keysNode, ok := node.GetOptionalChildByTag("keys")
|
||||
if !ok {
|
||||
keysNode = node
|
||||
}
|
||||
|
||||
identityKeyRaw, ok := keysNode.GetChildByTag("identity").Content.([]byte)
|
||||
if !ok || len(identityKeyRaw) != 32 {
|
||||
return nil, fmt.Errorf("invalid identity key in prekey response")
|
||||
}
|
||||
identityKeyPub := *(*[32]byte)(identityKeyRaw)
|
||||
|
||||
preKey, err := nodeToPreKey(keysNode.GetChildByTag("key"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid prekey in prekey response: %w", err)
|
||||
}
|
||||
signedPreKey, err := nodeToPreKey(keysNode.GetChildByTag("skey"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid signed prekey in prekey response: %w", err)
|
||||
}
|
||||
|
||||
return prekey.NewBundle(registrationID, deviceID,
|
||||
optional.NewOptionalUint32(preKey.KeyID), signedPreKey.KeyID,
|
||||
ecc.NewDjbECPublicKey(*preKey.Pub), ecc.NewDjbECPublicKey(*signedPreKey.Pub), *signedPreKey.Signature,
|
||||
identity.NewKey(ecc.NewDjbECPublicKey(identityKeyPub))), nil
|
||||
}
|
||||
|
||||
func nodeToPreKey(node waBinary.Node) (*keys.PreKey, error) {
|
||||
key := keys.PreKey{
|
||||
KeyPair: keys.KeyPair{},
|
||||
KeyID: 0,
|
||||
Signature: nil,
|
||||
}
|
||||
if id := node.GetChildByTag("id"); id.Tag != "id" {
|
||||
return nil, fmt.Errorf("prekey node doesn't contain ID tag")
|
||||
} else if idBytes, ok := id.Content.([]byte); !ok {
|
||||
return nil, fmt.Errorf("prekey ID has unexpected content (%T)", id.Content)
|
||||
} else if len(idBytes) != 3 {
|
||||
return nil, fmt.Errorf("prekey ID has unexpected number of bytes (%d, expected 3)", len(idBytes))
|
||||
} else {
|
||||
key.KeyID = binary.BigEndian.Uint32(append([]byte{0}, idBytes...))
|
||||
}
|
||||
if pubkey := node.GetChildByTag("value"); pubkey.Tag != "value" {
|
||||
return nil, fmt.Errorf("prekey node doesn't contain value tag")
|
||||
} else if pubkeyBytes, ok := pubkey.Content.([]byte); !ok {
|
||||
return nil, fmt.Errorf("prekey value has unexpected content (%T)", pubkey.Content)
|
||||
} else if len(pubkeyBytes) != 32 {
|
||||
return nil, fmt.Errorf("prekey value has unexpected number of bytes (%d, expected 32)", len(pubkeyBytes))
|
||||
} else {
|
||||
key.KeyPair.Pub = (*[32]byte)(pubkeyBytes)
|
||||
}
|
||||
if node.Tag == "skey" {
|
||||
if sig := node.GetChildByTag("signature"); sig.Tag != "signature" {
|
||||
return nil, fmt.Errorf("prekey node doesn't contain signature tag")
|
||||
} else if sigBytes, ok := sig.Content.([]byte); !ok {
|
||||
return nil, fmt.Errorf("prekey signature has unexpected content (%T)", sig.Content)
|
||||
} else if len(sigBytes) != 64 {
|
||||
return nil, fmt.Errorf("prekey signature has unexpected number of bytes (%d, expected 64)", len(sigBytes))
|
||||
} else {
|
||||
key.Signature = (*[64]byte)(sigBytes)
|
||||
}
|
||||
}
|
||||
return &key, nil
|
||||
}
|
||||
|
||||
func preKeysToNodes(prekeys []*keys.PreKey) []waBinary.Node {
|
||||
nodes := make([]waBinary.Node, len(prekeys))
|
||||
for i, key := range prekeys {
|
||||
nodes[i] = preKeyToNode(key)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
Reference in New Issue
Block a user