Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: changes on keyId generation #500

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/main/java/it/auties/whatsapp/api/Whatsapp.java
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ public CompletableFuture<? extends MessageInfo> sendReaction(MessageInfo message
*/
public CompletableFuture<? extends MessageInfo> sendReaction(MessageInfo message, String reaction) {
var key = new ChatMessageKeyBuilder()
.id(ChatMessageKey.randomId())
.id(ChatMessageKey.randomIdV2(message.senderJid(), store().clientType()))
.chatJid(message.parentJid())
.senderJid(message.senderJid())
.fromMe(Objects.equals(message.senderJid().toSimpleJid(), jidOrThrowError().toSimpleJid()))
Expand Down Expand Up @@ -574,7 +574,7 @@ public CompletableFuture<ChatMessageInfo> sendChatMessage(JidProvider recipient,
.deviceListMetadataVersion(2)
.build();
var key = new ChatMessageKeyBuilder()
.id(ChatMessageKey.randomId())
.id(ChatMessageKey.randomIdV2(jidOrThrowError(), store().clientType()))
.chatJid(recipient.toJid())
.fromMe(true)
.senderJid(jidOrThrowError())
Expand Down Expand Up @@ -688,7 +688,7 @@ public CompletableFuture<ChatMessageInfo> sendStatus(MessageContainer message) {
.deviceListMetadataVersion(2)
.build();
var key = new ChatMessageKeyBuilder()
.id(ChatMessageKey.randomId())
.id(ChatMessageKey.randomIdV2(jidOrThrowError(), store().clientType()))
.chatJid(Jid.of("status@broadcast"))
.fromMe(true)
.senderJid(jidOrThrowError())
Expand Down Expand Up @@ -1731,7 +1731,7 @@ public CompletableFuture<Void> deleteMessage(ChatMessageInfo messageInfo, boolea
.build();
var sender = messageInfo.chatJid().hasServer(JidServer.GROUP) ? jidOrThrowError() : null;
var key = new ChatMessageKeyBuilder()
.id(ChatMessageKey.randomId())
.id(ChatMessageKey.randomIdV2(sender, store().clientType()))
.chatJid(messageInfo.chatJid())
.fromMe(true)
.senderJid(sender)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import it.auties.protobuf.annotation.ProtobufProperty;
import it.auties.protobuf.model.ProtobufMessage;
import it.auties.protobuf.model.ProtobufType;
import it.auties.whatsapp.api.ClientType;
import it.auties.whatsapp.model.info.ChatMessageInfo;
import it.auties.whatsapp.model.jid.Jid;
import it.auties.whatsapp.model.jid.JidServer;
import it.auties.whatsapp.util.Bytes;

import java.util.HexFormat;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.*;

/**
* A container for unique identifiers and metadata linked to a {@link Message} and contained in
Expand All @@ -36,7 +39,7 @@ public final class ChatMessageKey implements ProtobufMessage {
public ChatMessageKey(Jid chatJid, boolean fromMe, String id, Jid senderJid) {
this.chatJid = chatJid;
this.fromMe = fromMe;
this.id = Objects.requireNonNullElseGet(id, ChatMessageKey::randomId);
this.id = Objects.requireNonNullElse(id, randomIdV2(senderJid));
this.senderJid = senderJid;
}

Expand All @@ -45,7 +48,7 @@ public ChatMessageKey(Jid chatJid, boolean fromMe) {
}

public ChatMessageKey(Jid chatJid, boolean fromMe, Jid senderJid) {
this(chatJid, fromMe, randomId(), senderJid);
this(chatJid, fromMe, randomIdV2(senderJid), senderJid);
}

/**
Expand All @@ -59,6 +62,77 @@ public static String randomId() {
.toUpperCase(Locale.ROOT);
}

/**
* Generates a random message id based on the Client Type. Generation methods are taken from WEB and Android code
* @param jid senderJid
* @param clientType clientType (Mobile or Web)
* @return a non-null String
*/

public static String randomIdV2(Jid jid, ClientType... clientType) {
var type = clientType.length == 0 ? ClientType.WEB : clientType[0];
return switch (type) {
case ClientType.WEB -> randomWebKeyId(jid);
case ClientType.MOBILE -> randomMobileKeyId(jid);
};
}

private static String randomWebKeyId(Jid jid) {
try {
var random = new Random();
var meJid = Objects.requireNonNullElse(jid, Jid.ofServer(JidServer.WHATSAPP));
var meUser = "%s@%s".formatted(meJid.user(), "@c.us");
long timeSeconds = Instant.now().getEpochSecond();
byte[] randomBytes = new byte[16];
random.nextBytes(randomBytes);
var buffer = ByteBuffer.allocate(Long.BYTES + meUser.length() + randomBytes.length);
buffer.putLong(timeSeconds);
buffer.put(meUser.getBytes());
buffer.put(randomBytes);
var digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(buffer.array());
byte[] truncatedHash = new byte[9];
System.arraycopy(hash, 0, truncatedHash, 0, 9);
return "3EB0" + HexFormat.of().formatHex(truncatedHash).toUpperCase();
} catch (NoSuchAlgorithmException e) {
return randomId();
}
}

private static String randomMobileKeyId(Jid jid) {
try {
var random = new Random();
var meJid = Objects.requireNonNullElse(jid, Jid.ofServer(JidServer.WHATSAPP));
var meUser = meJid.toSimpleJid().toString().getBytes();
var messageDigest = MessageDigest.getInstance("MD5");
long timeMillis = System.currentTimeMillis();
byte[] bArr = new byte[8];
for (int i = 7; i >= 0; i--) {
bArr[i] = (byte) timeMillis;
timeMillis >>= 8;
}
messageDigest.update(bArr);
messageDigest.update(meUser);
byte[] bArr2 = new byte[16];
random.nextBytes(bArr2);
messageDigest.update(bArr2);
var digested = messageDigest.digest();
char[] cArr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
char[] cArr2 = new char[digested.length * 2];
int i = 0;
for (byte b : digested) {
int i2 = b & 255;
int i3 = i + 1;
cArr2[i] = cArr[i2 >>> 4];
i = i3 + 1;
cArr2[i3] = cArr[i2 & 15];
}
return new String(cArr2);
} catch (NoSuchAlgorithmException e) {
return randomId();
}
}

public Jid chatJid() {
return chatJid;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/it/auties/whatsapp/socket/SocketHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ public CompletableFuture<Void> sendPeerMessage(Jid companion, ProtocolMessage me
var jid = store.jid()
.orElseThrow(() -> new IllegalStateException("The session isn't connected"));
var key = new ChatMessageKeyBuilder()
.id(ChatMessageKey.randomId())
.id(ChatMessageKey.randomIdV2(jid, store.clientType()))
.chatJid(companion)
.fromMe(true)
.senderJid(jid)
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/it/auties/whatsapp/TestLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,7 @@ public void testListMessage() {
.jid()
.orElseThrow();
var keyInfo = new ChatMessageKeyBuilder()
.id(ChatMessageKey.randomId())
.id(ChatMessageKey.randomIdV2(jid, api.store().clientType()))
.chatJid(contact)
.senderJid(jid)
.fromMe(true)
Expand Down
Loading