From 096839e6200d9d7e3100177e042be4cee38b0eeb Mon Sep 17 00:00:00 2001 From: Quishot WADEV <70950705+Darker935@users.noreply.github.com> Date: Sun, 16 Jun 2024 09:44:17 -0300 Subject: [PATCH 1/5] Changes on keyId generation This changes avoid some future problems with message ID's --- .../model/message/model/ChatMessageKey.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java index 85bb5334..5898b2b9 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java @@ -5,6 +5,7 @@ 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.util.Bytes; @@ -59,6 +60,75 @@ 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 = Objects.requireNonNullElse(clientType[0], ClientType.WEB); + 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 meUser = "%s@%s".formatted(jid.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); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + private static String randomMobileKeyId(Jid jid) { + try { + var random = new Random(); + var messageDigest = MessageDigest.getInstance("MD5"); + var meUser = jid.toSimpleJid().toString().getBytes(); + 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) { + throw new RuntimeException(e); + } + } + public Jid chatJid() { return chatJid; } From 965213a2806df3c2a7558269f4125eca44d329c9 Mon Sep 17 00:00:00 2001 From: Quishot WADEV <70950705+Darker935@users.noreply.github.com> Date: Sun, 16 Jun 2024 10:06:51 -0300 Subject: [PATCH 2/5] Invoking randomIdV2() --- src/main/java/it/auties/whatsapp/api/Whatsapp.java | 8 ++++---- .../model/message/model/ChatMessageKey.java | 13 +++++++------ .../it/auties/whatsapp/socket/SocketHandler.java | 2 +- src/test/java/it/auties/whatsapp/TestLibrary.java | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/java/it/auties/whatsapp/api/Whatsapp.java b/src/main/java/it/auties/whatsapp/api/Whatsapp.java index b8c05869..c7e1f849 100644 --- a/src/main/java/it/auties/whatsapp/api/Whatsapp.java +++ b/src/main/java/it/auties/whatsapp/api/Whatsapp.java @@ -411,7 +411,7 @@ public CompletableFuture sendReaction(MessageInfo message */ public CompletableFuture 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())) @@ -574,7 +574,7 @@ public CompletableFuture 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()) @@ -688,7 +688,7 @@ public CompletableFuture 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()) @@ -1731,7 +1731,7 @@ public CompletableFuture 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) diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java index 5898b2b9..18c46724 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java @@ -10,10 +10,11 @@ import it.auties.whatsapp.model.jid.Jid; 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 @@ -37,7 +38,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; } @@ -46,7 +47,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); } /** diff --git a/src/main/java/it/auties/whatsapp/socket/SocketHandler.java b/src/main/java/it/auties/whatsapp/socket/SocketHandler.java index 8759ba18..a57704a4 100644 --- a/src/main/java/it/auties/whatsapp/socket/SocketHandler.java +++ b/src/main/java/it/auties/whatsapp/socket/SocketHandler.java @@ -455,7 +455,7 @@ public CompletableFuture 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) diff --git a/src/test/java/it/auties/whatsapp/TestLibrary.java b/src/test/java/it/auties/whatsapp/TestLibrary.java index 8423d433..53a6bf72 100644 --- a/src/test/java/it/auties/whatsapp/TestLibrary.java +++ b/src/test/java/it/auties/whatsapp/TestLibrary.java @@ -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) From c614bea0081e20a3cc89cef8389e3cd0a89b9a9b Mon Sep 17 00:00:00 2001 From: Quishot WADEV <70950705+Darker935@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:47:39 -0300 Subject: [PATCH 3/5] fix: index 0 of length 0 --- .../it/auties/whatsapp/model/message/model/ChatMessageKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java index 18c46724..d00cf567 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java @@ -69,7 +69,7 @@ public static String randomId() { */ public static String randomIdV2(Jid jid, ClientType... clientType) { - var type = Objects.requireNonNullElse(clientType[0], ClientType.WEB); + var type = clientType.length == 0 ? ClientType.WEB : clientType[0]; return switch (type) { case ClientType.WEB -> randomWebKeyId(jid); case ClientType.MOBILE -> randomMobileKeyId(jid); From 2c43c6d64082ee1d01b86763d57e0f54a1406182 Mon Sep 17 00:00:00 2001 From: Quishot WADEV <70950705+Darker935@users.noreply.github.com> Date: Sun, 16 Jun 2024 22:14:09 -0300 Subject: [PATCH 4/5] Upper-case --- .../it/auties/whatsapp/model/message/model/ChatMessageKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java index d00cf567..ab188699 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java @@ -91,7 +91,7 @@ private static String randomWebKeyId(Jid jid) { byte[] hash = digest.digest(buffer.array()); byte[] truncatedHash = new byte[9]; System.arraycopy(hash, 0, truncatedHash, 0, 9); - return "3EB0" + HexFormat.of().formatHex(truncatedHash); + return "3EB0" + HexFormat.of().formatHex(truncatedHash).toUpperCase(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } From d4a8da2a1a65c64d20550c0f95e2d026d405f02a Mon Sep 17 00:00:00 2001 From: Quishot WADEV <70950705+Darker935@users.noreply.github.com> Date: Mon, 17 Jun 2024 09:36:37 -0300 Subject: [PATCH 5/5] Fix when JID is null --- .../whatsapp/model/message/model/ChatMessageKey.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java index ab188699..a9bb5249 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java @@ -8,6 +8,7 @@ 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.nio.ByteBuffer; @@ -79,7 +80,8 @@ public static String randomIdV2(Jid jid, ClientType... clientType) { private static String randomWebKeyId(Jid jid) { try { var random = new Random(); - var meUser = "%s@%s".formatted(jid.user(), "@c.us"); + 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); @@ -93,15 +95,16 @@ private static String randomWebKeyId(Jid jid) { System.arraycopy(hash, 0, truncatedHash, 0, 9); return "3EB0" + HexFormat.of().formatHex(truncatedHash).toUpperCase(); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(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"); - var meUser = jid.toSimpleJid().toString().getBytes(); long timeMillis = System.currentTimeMillis(); byte[] bArr = new byte[8]; for (int i = 7; i >= 0; i--) { @@ -126,7 +129,7 @@ private static String randomMobileKeyId(Jid jid) { } return new String(cArr2); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); + return randomId(); } }