/* * Copyright 2014 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 android.os.Build; import android.util.Log; import java.io.FileDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.Socket; import java.security.InvalidKeyException; import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.ECParameterSpec; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.X509TrustManager; /** * */ public class Platform { private static final String TAG = "Conscrypt"; private static Method m_getCurveName; static { try { m_getCurveName = ECParameterSpec.class.getDeclaredMethod("getCurveName"); m_getCurveName.setAccessible(true); } catch (Exception ignored) { } } public static void setup() { } public static FileDescriptor getFileDescriptor(Socket s) { try { Field f_impl = Socket.class.getDeclaredField("impl"); f_impl.setAccessible(true); Object socketImpl = f_impl.get(s); Class<?> c_socketImpl = Class.forName("java.net.SocketImpl"); Field f_fd = c_socketImpl.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 FileDescriptor getFileDescriptorFromSSLSocket(OpenSSLSocketImpl openSSLSocketImpl) { return getFileDescriptor(openSSLSocketImpl); } public static String getCurveName(ECParameterSpec spec) { if (m_getCurveName == null) { return null; } try { return (String) m_getCurveName.invoke(spec); } catch (Exception e) { return null; } } public static void setCurveName(ECParameterSpec spec, String curveName) { try { Method setCurveName = spec.getClass().getDeclaredMethod("setCurveName", String.class); setCurveName.invoke(spec, curveName); } catch (Exception ignored) { } } public static void setSocketTimeout(Socket s, long timeoutMillis) { // TODO: implement this for unbundled } public static void setEndpointIdentificationAlgorithm(SSLParameters params, String endpointIdentificationAlgorithm) { // TODO: implement this for unbundled } public static String getEndpointIdentificationAlgorithm(SSLParameters params) { // TODO: implement this for unbundled return null; } public static void checkServerTrusted(X509TrustManager x509tm, X509Certificate[] chain, String authType, String host) throws CertificateException { // TODO: use reflection to find whether we have TrustManagerImpl /* if (x509tm instanceof TrustManagerImpl) { TrustManagerImpl tm = (TrustManagerImpl) x509tm; tm.checkServerTrusted(chain, authType, host); } else { */ x509tm.checkServerTrusted(chain, authType); /* } */ } /** * Wraps an old AndroidOpenSSL key instance. This is not needed on platform * builds since we didn't backport, so return null. This code is from * Chromium's net/android/java/src/org/chromium/net/DefaultAndroidKeyStore.java */ public static OpenSSLKey wrapRsaKey(PrivateKey javaKey) throws InvalidKeyException { // This fixup only applies to pre-JB-MR1 if (Build.VERSION.SDK_INT >= 17) { return null; } // First, check that this is a proper instance of OpenSSLRSAPrivateKey // or one of its sub-classes. Class<?> superClass; try { superClass = Class .forName("org.apache.harmony.xnet.provider.jsse.OpenSSLRSAPrivateKey"); } catch (Exception e) { // This may happen if the target device has a completely different // implementation of the java.security APIs, compared to vanilla // Android. Highly unlikely, but still possible. Log.e(TAG, "Cannot find system OpenSSLRSAPrivateKey class: " + e); return null; } if (!superClass.isInstance(javaKey)) { // This may happen if the PrivateKey was not created by the // "AndroidOpenSSL" // provider, which should be the default. That could happen if an // OEM decided // to implement a different default provider. Also highly unlikely. Log.e(TAG, "Private key is not an OpenSSLRSAPrivateKey instance, its class name is:" + javaKey.getClass().getCanonicalName()); return null; } try { // Use reflection to invoke the 'getOpenSSLKey()' method on // the private key. This returns another Java object that wraps // a native EVP_PKEY. Note that the method is final, so calling // the superclass implementation is ok. Method getKey = superClass.getDeclaredMethod("getOpenSSLKey"); getKey.setAccessible(true); Object opensslKey = null; try { opensslKey = getKey.invoke(javaKey); } finally { getKey.setAccessible(false); } if (opensslKey == null) { // Bail when detecting OEM "enhancement". Log.e(TAG, "Could not getOpenSSLKey on instance: " + javaKey.toString()); return null; } // Use reflection to invoke the 'getPkeyContext' method on the // result of the getOpenSSLKey(). This is an 32-bit integer // which is the address of an EVP_PKEY object. Note that this // method these days returns a 64-bit long, but since this code // path is used for older Android versions, it may still return // a 32-bit int here. To be on the safe side, we cast the return // value via Number rather than directly to Integer or Long. Method getPkeyContext; try { getPkeyContext = opensslKey.getClass().getDeclaredMethod("getPkeyContext"); } catch (Exception e) { // Bail here too, something really not working as expected. Log.e(TAG, "No getPkeyContext() method on OpenSSLKey member:" + e); return null; } getPkeyContext.setAccessible(true); long evp_pkey = 0; try { evp_pkey = ((Number) getPkeyContext.invoke(opensslKey)).longValue(); } finally { getPkeyContext.setAccessible(false); } if (evp_pkey == 0) { // The PrivateKey is probably rotten for some reason. Log.e(TAG, "getPkeyContext() returned null"); return null; } return new OpenSSLKey(evp_pkey); } catch (Exception e) { Log.e(TAG, "Error during conversion of privatekey instance: " + javaKey.toString(), e); 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) { // Fail silently } } }