blob: da62bcd63fb6fe94d3b3e2cb1d8183e257e199c4 [file] [log] [blame]
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.conscrypt;
import static android.system.OsConstants.SOL_SOCKET;
import static android.system.OsConstants.SO_SNDTIMEO;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketImpl;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECParameterSpec;
import java.util.Collections;
import java.util.List;
import javax.crypto.spec.GCMParameterSpec;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SNIServerName;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.StandardConstants;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import libcore.net.NetworkSecurityPolicy;
import sun.security.x509.AlgorithmId;
class Platform {
private static class NoPreloadHolder {
public static final Platform MAPPER = new Platform();
}
/**
* Runs all the setup for the platform that only needs to run once.
*/
public static void setup() {
NoPreloadHolder.MAPPER.ping();
}
/**
* Just a placeholder to make sure the class is initialized.
*/
private void ping() {
}
private Platform() {
}
public static FileDescriptor getFileDescriptor(Socket s) {
return s.getFileDescriptor$();
}
public static FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) {
try {
Field f_impl = Socket.class.getDeclaredField("impl");
f_impl.setAccessible(true);
Object socketImpl = f_impl.get(openSSLSocketImpl);
Field f_fd = SocketImpl.class.getDeclaredField("fd");
f_fd.setAccessible(true);
return (FileDescriptor) f_fd.get(socketImpl);
} catch (Exception e) {
throw new RuntimeException("Can't get FileDescriptor from socket", e);
}
}
public static String getCurveName(ECParameterSpec spec) {
return spec.getCurveName();
}
public static void setCurveName(ECParameterSpec spec, String curveName) {
spec.setCurveName(curveName);
}
public static void setSocketWriteTimeout(Socket s, long timeoutMillis) throws SocketException {
StructTimeval tv = StructTimeval.fromMillis(timeoutMillis);
try {
Os.setsockoptTimeval(s.getFileDescriptor$(), SOL_SOCKET, SO_SNDTIMEO, tv);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsSocketException();
}
}
public static void setSSLParameters(SSLParameters params, SSLParametersImpl impl,
OpenSSLSocketImpl socket) {
impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
List<SNIServerName> serverNames = params.getServerNames();
if (serverNames != null) {
for (SNIServerName serverName : serverNames) {
if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
socket.setHostname(((SNIHostName) serverName).getAsciiName());
break;
}
}
}
}
public static void getSSLParameters(SSLParameters params, SSLParametersImpl impl,
OpenSSLSocketImpl socket) {
params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
if (impl.getUseSni() && AddressUtils.isValidSniHostname(socket.getHostname())) {
params.setServerNames(Collections.<SNIServerName> singletonList(
new SNIHostName(socket.getHostname())));
}
}
public static void setSSLParameters(
SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
impl.setEndpointIdentificationAlgorithm(params.getEndpointIdentificationAlgorithm());
impl.setUseCipherSuitesOrder(params.getUseCipherSuitesOrder());
List<SNIServerName> serverNames = params.getServerNames();
if (serverNames != null) {
for (SNIServerName serverName : serverNames) {
if (serverName.getType() == StandardConstants.SNI_HOST_NAME) {
engine.setSniHostname(((SNIHostName) serverName).getAsciiName());
break;
}
}
}
}
public static void getSSLParameters(
SSLParameters params, SSLParametersImpl impl, OpenSSLEngineImpl engine) {
params.setEndpointIdentificationAlgorithm(impl.getEndpointIdentificationAlgorithm());
params.setUseCipherSuitesOrder(impl.getUseCipherSuitesOrder());
if (impl.getUseSni() && AddressUtils.isValidSniHostname(engine.getSniHostname())) {
params.setServerNames(Collections.<SNIServerName>singletonList(
new SNIHostName(engine.getSniHostname())));
}
}
/**
* Helper function to unify calls to the different names used for each function taking a
* Socket, SSLEngine, or String (legacy Android).
*/
private static boolean checkTrusted(String methodName, X509TrustManager tm,
X509Certificate[] chain, String authType, Class<?> argumentClass,
Object argumentInstance) throws CertificateException {
// Use duck-typing to try and call the hostname-aware method if available.
try {
Method method = tm.getClass().getMethod(methodName,
X509Certificate[].class,
String.class,
argumentClass);
method.invoke(tm, chain, authType, argumentInstance);
return true;
} catch (NoSuchMethodException | IllegalAccessException ignored) {
} catch (InvocationTargetException e) {
if (e.getCause() instanceof CertificateException) {
throw (CertificateException) e.getCause();
}
throw new RuntimeException(e.getCause());
}
return false;
}
public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
String authType, OpenSSLSocketImpl socket) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkClientTrusted(chain, authType, socket);
} else if (!checkTrusted("checkClientTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
socket.getHandshakeSession().getPeerHost())) {
tm.checkClientTrusted(chain, authType);
}
}
public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
String authType, OpenSSLSocketImpl socket) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, socket);
} else if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
socket.getHandshakeSession().getPeerHost())) {
tm.checkServerTrusted(chain, authType);
}
}
public static void checkClientTrusted(X509TrustManager tm, X509Certificate[] chain,
String authType, OpenSSLEngineImpl engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkClientTrusted(chain, authType, engine);
} else if (!checkTrusted("checkClientTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkClientTrusted", tm, chain, authType, String.class,
engine.getHandshakeSession().getPeerHost())) {
tm.checkClientTrusted(chain, authType);
}
}
public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
String authType, OpenSSLEngineImpl engine) throws CertificateException {
if (tm instanceof X509ExtendedTrustManager) {
X509ExtendedTrustManager x509etm = (X509ExtendedTrustManager) tm;
x509etm.checkServerTrusted(chain, authType, engine);
} else if (!checkTrusted("checkServerTrusted", tm, chain, authType, SSLEngine.class, engine)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
engine.getHandshakeSession().getPeerHost())) {
tm.checkServerTrusted(chain, authType);
}
}
/**
* Wraps an old AndroidOpenSSL key instance. This is not needed on platform
* builds since we didn't backport, so return null.
*/
public static OpenSSLKey wrapRsaKey(PrivateKey key) {
return null;
}
/**
* Logs to the system EventLog system.
*/
public static void logEvent(String message) {
try {
Class processClass = Class.forName("android.os.Process");
Object processInstance = processClass.newInstance();
Method myUidMethod = processClass.getMethod("myUid", (Class[]) null);
int uid = (Integer) myUidMethod.invoke(processInstance);
Class eventLogClass = Class.forName("android.util.EventLog");
Object eventLogInstance = eventLogClass.newInstance();
Method writeEventMethod = eventLogClass.getMethod("writeEvent",
new Class[] { Integer.TYPE, Object[].class });
writeEventMethod.invoke(eventLogInstance, 0x534e4554 /* SNET */,
new Object[] { "conscrypt", uid, message });
} catch (Exception e) {
// Do not log and fail silently
}
}
/**
* Returns true if the supplied hostname is an literal IP address.
*/
public static boolean isLiteralIpAddress(String hostname) {
return InetAddress.isNumeric(hostname);
}
/**
* Wrap the SocketFactory with the platform wrapper if needed for compatability.
* For the platform-bundled library we never need to wrap.
*/
public static SSLSocketFactory wrapSocketFactoryIfNeeded(OpenSSLSocketFactoryImpl factory) {
return factory;
}
/**
* Convert from platform's GCMParameterSpec to our internal version.
*/
public static GCMParameters fromGCMParameterSpec(AlgorithmParameterSpec params) {
if (params instanceof GCMParameterSpec) {
GCMParameterSpec gcmParams = (GCMParameterSpec) params;
return new GCMParameters(gcmParams.getTLen(), gcmParams.getIV());
}
return null;
}
/**
* Creates a platform version of {@code GCMParameterSpec}.
*/
public static AlgorithmParameterSpec toGCMParameterSpec(int tagLenInBits, byte[] iv) {
return new GCMParameterSpec(tagLenInBits, iv);
}
/*
* CloseGuard functions.
*/
public static CloseGuard closeGuardGet() {
return CloseGuard.get();
}
public static void closeGuardOpen(Object guardObj, String message) {
CloseGuard guard = (CloseGuard) guardObj;
guard.open(message);
}
public static void closeGuardClose(Object guardObj) {
CloseGuard guard = (CloseGuard) guardObj;
guard.close();
}
public static void closeGuardWarnIfOpen(Object guardObj) {
CloseGuard guard = (CloseGuard) guardObj;
guard.warnIfOpen();
}
/*
* BlockGuard functions.
*/
public static void blockGuardOnNetwork() {
BlockGuard.getThreadPolicy().onNetwork();
}
/**
* OID to Algorithm Name mapping.
*/
public static String oidToAlgorithmName(String oid) {
try {
return AlgorithmId.get(oid).getName();
} catch (NoSuchAlgorithmException e) {
return oid;
}
}
/*
* Pre-Java 8 backward compatibility.
*/
public static SSLSession wrapSSLSession(AbstractOpenSSLSession sslSession) {
return new OpenSSLExtendedSessionImpl(sslSession);
}
public static SSLSession unwrapSSLSession(SSLSession sslSession) {
if (sslSession instanceof OpenSSLExtendedSessionImpl) {
return ((OpenSSLExtendedSessionImpl) sslSession).getDelegate();
}
return sslSession;
}
/*
* Pre-Java-7 backward compatibility.
*/
public static String getHostStringFromInetSocketAddress(InetSocketAddress addr) {
return addr.getHostString();
}
public static boolean isCTVerificationRequired(String hostname) {
return NetworkSecurityPolicy.getInstance()
.isCertificateTransparencyVerificationRequired(hostname);
}
}