Skip to content

Commit e9921b7

Browse files
committed
Create ConscryptLoader for code sharing
1 parent f51336f commit e9921b7

File tree

3 files changed

+93
-55
lines changed

3 files changed

+93
-55
lines changed

alts/src/main/java/io/grpc/alts/internal/AesGcmAeadCrypter.java

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import static com.google.common.base.Preconditions.checkArgument;
2020

21-
import java.lang.reflect.Method;
21+
import io.grpc.internal.ConscryptLoader;
2222
import java.nio.ByteBuffer;
2323
import java.security.GeneralSecurityException;
2424
import java.security.Provider;
@@ -111,51 +111,21 @@ static int getKeyLength() {
111111
}
112112

113113
private static Provider getConscrypt() {
114-
// This is equivalent to if (Conscrypt.isAvailable()) return Conscrypt.newProvider();
115-
114+
if (!ConscryptLoader.isPresent()) {
115+
return null;
116+
}
116117
// Conscrypt 2.1.0 or later is required. If an older version is used, it will fail with these
117118
// sorts of errors:
118119
// "The underlying Cipher implementation does not support this method"
119120
// "error:1e000067:Cipher functions:OPENSSL_internal:BUFFER_TOO_SMALL"
120121
//
121122
// While we could use Conscrypt.version() to check compatibility, that is _very_ verbose via
122123
// reflection. In practice, old conscrypts are probably not much of a problem.
123-
Class<?> conscryptClass;
124124
try {
125-
conscryptClass = Class.forName("org.conscrypt.Conscrypt");
126-
} catch (ClassNotFoundException ex) {
127-
logger.log(Level.FINE, "Could not find Conscrypt", ex);
128-
return null;
129-
}
130-
Method method;
131-
try {
132-
method = conscryptClass.getMethod("newProvider");
133-
} catch (SecurityException ex) {
134-
logger.log(Level.FINE, "Could not find Conscrypt factory method", ex);
135-
return null;
136-
} catch (NoSuchMethodException ex) {
137-
logger.log(Level.WARNING, "Could not find Conscrypt factory method", ex);
138-
return null;
139-
}
140-
Object provider;
141-
try {
142-
provider = method.invoke(null);
143-
} catch (IllegalAccessException ex) {
144-
logger.log(Level.WARNING, "Could not call Conscrypt factory method", ex);
145-
return null;
146-
} catch (Throwable ex) {
147-
// This is probably an InvocationTargetException, which means something's wrong with the JNI
148-
// loading. Maybe the platform is not supported. We could have used Conscrypt.isAvailable(),
149-
// but it just catches Throwable as well
150-
logger.log(Level.WARNING, "Failed calling Conscrypt factory method", ex);
151-
return null;
152-
}
153-
if (!(provider instanceof Provider)) {
154-
logger.log(
155-
Level.WARNING, "Could not load Conscrypt. Returned provider was not a Provider: {0}",
156-
provider.getClass().getName());
125+
return ConscryptLoader.newProvider();
126+
} catch (Throwable t) {
127+
logger.log(Level.INFO, "Could not load Conscrypt. Will use slower JDK implementation", t);
157128
return null;
158129
}
159-
return (Provider) provider;
160130
}
161131
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2019 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import java.lang.reflect.InvocationTargetException;
20+
import java.lang.reflect.Method;
21+
import java.security.Provider;
22+
23+
/**
24+
* Utility to load dynamically Conscrypt when it is available.
25+
*/
26+
public final class ConscryptLoader {
27+
private static final Method NEW_PROVIDER_METHOD;
28+
private static final Method IS_CONSCRYPT_METHOD;
29+
30+
static {
31+
Method newProvider;
32+
Method isConscrypt;
33+
try {
34+
Class<?> conscryptClass = Class.forName("org.conscrypt.Conscrypt");
35+
newProvider = conscryptClass.getMethod("newProvider");
36+
isConscrypt = conscryptClass.getMethod("isConscrypt", Provider.class);
37+
} catch (ClassNotFoundException ex) {
38+
newProvider = null;
39+
isConscrypt = null;
40+
} catch (NoSuchMethodException ex) {
41+
throw new AssertionError(ex);
42+
}
43+
NEW_PROVIDER_METHOD = newProvider;
44+
IS_CONSCRYPT_METHOD = isConscrypt;
45+
}
46+
47+
/**
48+
* Returns {@code true} when the Conscrypt Java classes are available. Does not imply it actually
49+
* works on this platform.
50+
*/
51+
public static boolean isPresent() {
52+
return NEW_PROVIDER_METHOD != null;
53+
}
54+
55+
/** Same as {@code Conscrypt.isConscrypt(Provider)}. */
56+
public static boolean isConscrypt(Provider provider) {
57+
if (!isPresent()) {
58+
return false;
59+
}
60+
try {
61+
return (Boolean) IS_CONSCRYPT_METHOD.invoke(null, provider);
62+
} catch (IllegalAccessException ex) {
63+
throw new AssertionError(ex);
64+
} catch (InvocationTargetException ex) {
65+
throw new AssertionError(ex);
66+
}
67+
}
68+
69+
/** Same as {@code Conscrypt.newProvider()}. */
70+
public static Provider newProvider() throws Throwable {
71+
if (!isPresent()) {
72+
Class.forName("org.conscrypt.Conscrypt");
73+
throw new AssertionError("Unexpected failure referencing Conscrypt class");
74+
}
75+
// Exceptions here probably mean something's wrong with the JNI loading. Maybe the platform is
76+
// not supported. It's an error, but it may occur in some environments as part of normal
77+
// operation. It's too hard to distinguish "normal" from "abnormal" failures here.
78+
return (Provider) NEW_PROVIDER_METHOD.invoke(null);
79+
}
80+
}

testing/src/main/java/io/grpc/internal/testing/TestUtils.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package io.grpc.internal.testing;
1818

1919
import com.google.common.base.Throwables;
20+
import io.grpc.internal.ConscryptLoader;
2021
import java.io.BufferedInputStream;
2122
import java.io.BufferedOutputStream;
2223
import java.io.File;
@@ -25,8 +26,6 @@
2526
import java.io.IOException;
2627
import java.io.InputStream;
2728
import java.io.OutputStream;
28-
import java.lang.reflect.InvocationTargetException;
29-
import java.lang.reflect.Method;
3029
import java.net.InetAddress;
3130
import java.net.InetSocketAddress;
3231
import java.net.UnknownHostException;
@@ -163,33 +162,22 @@ public static void installConscryptIfAvailable() {
163162
conscryptInstallAttempted = true;
164163
return;
165164
}
166-
Class<?> conscrypt;
167-
try {
168-
conscrypt = Class.forName("org.conscrypt.Conscrypt");
169-
} catch (ClassNotFoundException ex) {
165+
if (!ConscryptLoader.isPresent()) {
170166
conscryptInstallAttempted = true;
171167
return;
172168
}
173-
Method newProvider;
174-
try {
175-
newProvider = conscrypt.getMethod("newProvider");
176-
} catch (NoSuchMethodException ex) {
177-
throw new RuntimeException("Could not find newProvider method on Conscrypt", ex);
178-
}
179169
Provider provider;
180170
try {
181-
provider = (Provider) newProvider.invoke(null);
182-
} catch (IllegalAccessException ex) {
183-
throw new RuntimeException("Could not invoke Conscrypt.newProvider", ex);
184-
} catch (InvocationTargetException ex) {
185-
Throwable root = Throwables.getRootCause(ex);
171+
provider = ConscryptLoader.newProvider();
172+
} catch (Throwable t) {
173+
Throwable root = Throwables.getRootCause(t);
186174
// Conscrypt uses a newer version of glibc than available on RHEL 6
187175
if (root instanceof UnsatisfiedLinkError && root.getMessage() != null
188176
&& root.getMessage().contains("GLIBC_2.14")) {
189177
conscryptInstallAttempted = true;
190178
return;
191179
}
192-
throw new RuntimeException("Could not invoke Conscrypt.newProvider", ex);
180+
throw new RuntimeException("Could not create Conscrypt provider", t);
193181
}
194182
Security.addProvider(provider);
195183
conscryptInstallAttempted = true;

0 commit comments

Comments
 (0)