From deb8c0c2bd6fa7a16c7e2480a554c044bba3371c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Hallingstad?= Date: Tue, 21 Jan 2025 10:45:54 +0100 Subject: [PATCH] Add hex formatting of MD5 hash --- vespajlib/abi-spec.json | 1 + vespajlib/src/main/java/com/yahoo/collections/MD5.java | 6 ++++++ .../src/main/java/com/yahoo/text/StringUtilities.java | 10 ++++++++++ .../test/java/com/yahoo/collections/MD5TestCase.java | 7 +++++++ 4 files changed, 24 insertions(+) diff --git a/vespajlib/abi-spec.json b/vespajlib/abi-spec.json index de020bf85471..28e9a2199b6c 100644 --- a/vespajlib/abi-spec.json +++ b/vespajlib/abi-spec.json @@ -3343,6 +3343,7 @@ ], "methods" : [ "public void ()", + "public static java.lang.String toHex(byte[])", "public static java.lang.String escape(java.lang.String)", "public static java.lang.String escape(java.lang.String, char)", "public static java.lang.String unescape(java.lang.String)", diff --git a/vespajlib/src/main/java/com/yahoo/collections/MD5.java b/vespajlib/src/main/java/com/yahoo/collections/MD5.java index 6bd32b62b745..52cb3b91e2e7 100644 --- a/vespajlib/src/main/java/com/yahoo/collections/MD5.java +++ b/vespajlib/src/main/java/com/yahoo/collections/MD5.java @@ -1,6 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.collections; +import com.yahoo.text.StringUtilities; import com.yahoo.text.Utf8; import java.security.MessageDigest; @@ -69,4 +70,9 @@ public byte[] hashFull(String s) { return digester.digest(Utf8.toBytes(s)); } + /** Returns the 32 byte representation of the MD5 hash of the given string. Compatible with md5sum(1). */ + public String hashToHex(String s) { + return StringUtilities.toHex(hashFull(s)); + } + } diff --git a/vespajlib/src/main/java/com/yahoo/text/StringUtilities.java b/vespajlib/src/main/java/com/yahoo/text/StringUtilities.java index 025eeba3998f..9760ea27e3fd 100644 --- a/vespajlib/src/main/java/com/yahoo/text/StringUtilities.java +++ b/vespajlib/src/main/java/com/yahoo/text/StringUtilities.java @@ -22,6 +22,16 @@ public class StringUtilities { private static byte toHex(int val) { return (byte) (val < 10 ? '0' + val : 'a' + (val - 10)); } + /** Returns a String where each byte b in `bytes` is replaced by "%02x".formatted(b). */ + public static String toHex(byte[] bytes) { + byte[] encoded = new byte[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++) { + encoded[i * 2] = toHex((bytes[i] >> 4) & 0xF); + encoded[i * 2 + 1] = toHex(bytes[i] & 0xF); + } + return new String(encoded, StandardCharsets.US_ASCII); + } + private static class ReplacementCharacters { public byte[] needEscape = new byte[256]; diff --git a/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java b/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java index 28cd3c90b905..3d44c9ef9d41 100644 --- a/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java +++ b/vespajlib/src/test/java/com/yahoo/collections/MD5TestCase.java @@ -11,6 +11,13 @@ */ public class MD5TestCase { + @Test + public void testHashToHex() { + MD5 md5 = new MD5(); + // Matches output from `md5sum <<< hello` + assertEquals("b1946ac92492d2347c6235b4d2611184", md5.hashToHex("hello\n")); + } + @Test public void testMD5() { MD5 md5 = new MD5();