fix `isBlockedMediaSha1` crash on malformed SHA1

Phillip Davis created

Change summary

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java     |  4 
src/test/java/eu/siacs/conversations/services/XmppConnectionServiceTest.java | 51 
2 files changed, 54 insertions(+), 1 deletion(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -512,7 +512,9 @@ public class XmppConnectionService extends Service {
     public boolean isBlockedMediaSha1(@Nullable final String sha1sum) {
         if (sha1sum == null) return false;
         try {
-            return isBlockedMedia(CryptoHelper.cid(CryptoHelper.hexToBytes(sha1sum), "sha-1"));
+            final byte[] bytes = CryptoHelper.hexToBytes(sha1sum);
+            if (bytes.length != 20) return false;
+            return isBlockedMedia(CryptoHelper.cid(bytes, "sha-1"));
         } catch (final NoSuchAlgorithmException e) {
             return false;
         }

src/test/java/eu/siacs/conversations/services/XmppConnectionServiceTest.java 🔗

@@ -0,0 +1,51 @@
+package eu.siacs.conversations.services;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.Build;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.ConscryptMode;
+
+import eu.siacs.conversations.Conversations;
+import io.ipfs.cid.Cid;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = Build.VERSION_CODES.TIRAMISU, application = Conversations.class)
+@ConscryptMode(ConscryptMode.Mode.OFF)
+public class XmppConnectionServiceTest {
+
+    private XmppConnectionService service;
+
+    @Before
+    public void setUp() {
+        service = mock(XmppConnectionService.class);
+        when(service.isBlockedMediaSha1(any())).thenCallRealMethod();
+        when(service.isBlockedMedia(any(Cid.class))).thenReturn(false);
+    }
+
+    @Test
+    public void testIsBlockedMediaSha1ReturnsFalseForNonSha1Length() {
+        // 36 hex chars = 18 bytes; valid SHA-1 is 40 hex chars = 20 bytes.
+        // Reproduces: IllegalStateException: Incorrect hash length: 18 != 20
+        assertFalse(service.isBlockedMediaSha1("aabbccddee1122334455667788990011aabb"));
+    }
+
+    @Test
+    public void testIsBlockedMediaSha1ReturnsFalseForNull() {
+        assertFalse(service.isBlockedMediaSha1(null));
+    }
+
+    @Test
+    public void testIsBlockedMediaSha1AcceptsValidSha1() {
+        // 40 hex chars = 20 bytes = valid SHA-1
+        assertFalse(service.isBlockedMediaSha1("aabbccddee112233445566778899001122334455"));
+    }
+}