Skip to content

Commit fd8bfc4

Browse files
committed
Lazily load ES|QL md5 function
In Java 14 the `MessageDigest` specification was changed so that the "MD5" hash function is no longer required. It is permissible for a JRE to ship without support for MD5 hashes. This commit modifies the ES|QL MD5 hash function implementation so that the `MessageDigest` object is no longer loaded on startup, and instead is lazily instantiated when needed. If an ES|QL query makes use of md5, and it is unavailable, then the query will fail with an ES|QL verification exception Resolves: #129689
1 parent 77b459c commit fd8bfc4

File tree

2 files changed

+57
-2
lines changed
  • x-pack/plugin/esql/src
    • main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string
    • test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string

2 files changed

+57
-2
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/Md5.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
1111
import org.elasticsearch.common.io.stream.StreamInput;
12+
import org.elasticsearch.xpack.esql.VerificationException;
1213
import org.elasticsearch.xpack.esql.core.expression.Expression;
1314
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
1415
import org.elasticsearch.xpack.esql.core.tree.Source;
@@ -18,13 +19,16 @@
1819
import org.elasticsearch.xpack.esql.expression.function.scalar.string.Hash.HashFunction;
1920

2021
import java.io.IOException;
22+
import java.security.MessageDigest;
23+
import java.security.NoSuchAlgorithmException;
2124
import java.util.List;
25+
import java.util.concurrent.atomic.AtomicReference;
2226

2327
public class Md5 extends AbstractHashFunction {
2428

2529
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "MD5", Md5::new);
2630

27-
private static final HashFunction MD5 = HashFunction.create("MD5");
31+
private final AtomicReference<HashFunction> MD5 = new AtomicReference<>();
2832

2933
@FunctionInfo(
3034
returnType = "keyword",
@@ -39,9 +43,23 @@ private Md5(StreamInput in) throws IOException {
3943
super(in);
4044
}
4145

46+
/**
47+
* As of Java 14, it is permissible for a JRE to ship without the {@code MD5} {@link MessageDigest}.
48+
* We want the "md5" function in ES|QL to fail at runtime on such platforms (rather than at startup)
49+
* so we build the {@link HashFunction} lazily.
50+
*/
4251
@Override
4352
protected HashFunction getHashFunction() {
44-
return MD5;
53+
HashFunction function = MD5.get();
54+
if (function == null) {
55+
try {
56+
function = new HashFunction("MD5", MessageDigest.getInstance("MD5"));
57+
MD5.compareAndSet(null, function);
58+
} catch (NoSuchAlgorithmException e) {
59+
throw new VerificationException("function 'md5' is not available on this platform: {}", e.getMessage());
60+
}
61+
}
62+
return function;
4563
}
4664

4765
@Override
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.esql.expression.function.scalar.string;
9+
10+
import org.elasticsearch.test.ESTestCase;
11+
import org.elasticsearch.xpack.esql.VerificationException;
12+
import org.elasticsearch.xpack.esql.core.expression.Literal;
13+
import org.elasticsearch.xpack.esql.core.tree.Source;
14+
15+
import java.security.Provider;
16+
import java.security.Security;
17+
18+
import static org.hamcrest.Matchers.notNullValue;
19+
20+
public class Md5UnavailableTests extends ESTestCase {
21+
22+
public void testMd5Unavailable() {
23+
assumeFalse("We run with different security providers in FIPS, and changing them at runtime is more complicated", inFipsJvm());
24+
final Provider sunProvider = Security.getProvider("SUN");
25+
final Md5 md5;
26+
try {
27+
Security.removeProvider("SUN");
28+
md5 = new Md5(Source.EMPTY, Literal.NULL);
29+
expectThrows(VerificationException.class, md5::getHashFunction);
30+
} finally {
31+
Security.addProvider(sunProvider);
32+
}
33+
34+
assertThat(md5.getHashFunction(), notNullValue());
35+
}
36+
37+
}

0 commit comments

Comments
 (0)