diff options
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/handshake.go')
-rw-r--r-- | vendor/go.mau.fi/whatsmeow/handshake.go | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/handshake.go b/vendor/go.mau.fi/whatsmeow/handshake.go new file mode 100644 index 00000000..ecd0398d --- /dev/null +++ b/vendor/go.mau.fi/whatsmeow/handshake.go @@ -0,0 +1,131 @@ +// 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 ( + "bytes" + "fmt" + "time" + + "google.golang.org/protobuf/proto" + + waProto "go.mau.fi/whatsmeow/binary/proto" + "go.mau.fi/whatsmeow/socket" + "go.mau.fi/whatsmeow/util/keys" +) + +const NoiseHandshakeResponseTimeout = 20 * time.Second + +// doHandshake implements the Noise_XX_25519_AESGCM_SHA256 handshake for the WhatsApp web API. +func (cli *Client) doHandshake(fs *socket.FrameSocket, ephemeralKP keys.KeyPair) error { + nh := socket.NewNoiseHandshake() + nh.Start(socket.NoiseStartPattern, fs.Header) + nh.Authenticate(ephemeralKP.Pub[:]) + data, err := proto.Marshal(&waProto.HandshakeMessage{ + ClientHello: &waProto.ClientHello{ + Ephemeral: ephemeralKP.Pub[:], + }, + }) + if err != nil { + return fmt.Errorf("failed to marshal handshake message: %w", err) + } + err = fs.SendFrame(data) + if err != nil { + return fmt.Errorf("failed to send handshake message: %w", err) + } + var resp []byte + select { + case resp = <-fs.Frames: + case <-time.After(NoiseHandshakeResponseTimeout): + return fmt.Errorf("timed out waiting for handshake response") + } + var handshakeResponse waProto.HandshakeMessage + err = proto.Unmarshal(resp, &handshakeResponse) + if err != nil { + return fmt.Errorf("failed to unmarshal handshake response: %w", err) + } + serverEphemeral := handshakeResponse.GetServerHello().GetEphemeral() + serverStaticCiphertext := handshakeResponse.GetServerHello().GetStatic() + certificateCiphertext := handshakeResponse.GetServerHello().GetPayload() + if len(serverEphemeral) != 32 || serverStaticCiphertext == nil || certificateCiphertext == nil { + return fmt.Errorf("missing parts of handshake response") + } + serverEphemeralArr := *(*[32]byte)(serverEphemeral) + + nh.Authenticate(serverEphemeral) + err = nh.MixSharedSecretIntoKey(*ephemeralKP.Priv, serverEphemeralArr) + if err != nil { + return fmt.Errorf("failed to mix server ephemeral key in: %w", err) + } + + staticDecrypted, err := nh.Decrypt(serverStaticCiphertext) + if err != nil { + return fmt.Errorf("failed to decrypt server static ciphertext: %w", err) + } else if len(staticDecrypted) != 32 { + return fmt.Errorf("unexpected length of server static plaintext %d (expected 32)", len(staticDecrypted)) + } + err = nh.MixSharedSecretIntoKey(*ephemeralKP.Priv, *(*[32]byte)(staticDecrypted)) + if err != nil { + return fmt.Errorf("failed to mix server static key in: %w", err) + } + + certDecrypted, err := nh.Decrypt(certificateCiphertext) + if err != nil { + return fmt.Errorf("failed to decrypt noise certificate ciphertext: %w", err) + } + var cert waProto.NoiseCertificate + err = proto.Unmarshal(certDecrypted, &cert) + if err != nil { + return fmt.Errorf("failed to unmarshal noise certificate: %w", err) + } + certDetailsRaw := cert.GetDetails() + certSignature := cert.GetSignature() + if certDetailsRaw == nil || certSignature == nil { + return fmt.Errorf("missing parts of noise certificate") + } + var certDetails waProto.NoiseCertificateDetails + err = proto.Unmarshal(certDetailsRaw, &certDetails) + if err != nil { + return fmt.Errorf("failed to unmarshal noise certificate details: %w", err) + } else if !bytes.Equal(certDetails.GetKey(), staticDecrypted) { + return fmt.Errorf("cert key doesn't match decrypted static") + } + + encryptedPubkey := nh.Encrypt(cli.Store.NoiseKey.Pub[:]) + err = nh.MixSharedSecretIntoKey(*cli.Store.NoiseKey.Priv, serverEphemeralArr) + if err != nil { + return fmt.Errorf("failed to mix noise private key in: %w", err) + } + + clientFinishPayloadBytes, err := proto.Marshal(cli.Store.GetClientPayload()) + if err != nil { + return fmt.Errorf("failed to marshal client finish payload: %w", err) + } + encryptedClientFinishPayload := nh.Encrypt(clientFinishPayloadBytes) + data, err = proto.Marshal(&waProto.HandshakeMessage{ + ClientFinish: &waProto.ClientFinish{ + Static: encryptedPubkey, + Payload: encryptedClientFinishPayload, + }, + }) + if err != nil { + return fmt.Errorf("failed to marshal handshake finish message: %w", err) + } + err = fs.SendFrame(data) + if err != nil { + return fmt.Errorf("failed to send handshake finish message: %w", err) + } + + ns, err := nh.Finish(fs, cli.handleFrame, cli.onDisconnect) + if err != nil { + return fmt.Errorf("failed to create noise socket: %w", err) + } + + cli.socket = ns + + return nil +} |