1/*
2 * Copyright 2014 The Android Open Source Project
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
17package org.conscrypt;
18
19import java.lang.reflect.Method;
20import java.net.Socket;
21import javax.crypto.SecretKey;
22import javax.net.ssl.SSLEngine;
23
24/**
25 * Reflection-based {@link PSKKeyManager} adaptor for objects which expose all the methods of the
26 * {@code PSKKeyManager} interface but do not implement the interface.
27 *
28 * <p>This is expected to be useful on platforms where there are multiple instances of the
29 * {@code PSKKeyManager} interface.
30 *
31 * Visible for testing only.
32 *
33 * @deprecated This abstraction is deprecated because it does not work with TLS 1.3.
34 */
35@Deprecated
36final class DuckTypedPSKKeyManager implements PSKKeyManager {
37
38    private final Object mDelegate;
39
40    private DuckTypedPSKKeyManager(Object delegate) {
41        mDelegate = delegate;
42    }
43
44    /**
45     * Gets an instance of {@code DuckTypedPSKKeyManager} which delegates all invocations of methods
46     * of the {@link PSKKeyManager} interface to the same methods of the provided object.
47     *
48     * @throws NoSuchMethodException if {@code obj} does not implement a method of the
49     *         {@code PSKKeyManager} interface.
50     */
51    static DuckTypedPSKKeyManager getInstance(Object obj) throws NoSuchMethodException {
52        Class<?> sourceClass = obj.getClass();
53        for (Method targetMethod : PSKKeyManager.class.getMethods()) {
54            if (targetMethod.isSynthetic()) {
55                continue;
56            }
57            // Check that obj exposes the target method (same name and parameter types)
58            Method sourceMethod =
59                    sourceClass.getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
60            // Check that the return type of obj's method matches the target method.
61            Class<?> sourceReturnType = sourceMethod.getReturnType();
62            Class<?> targetReturnType = targetMethod.getReturnType();
63            if (!targetReturnType.isAssignableFrom(sourceReturnType)) {
64                throw new NoSuchMethodException(sourceMethod + " return value (" + sourceReturnType
65                        + ") incompatible with target return value (" + targetReturnType + ")");
66            }
67        }
68
69        return new DuckTypedPSKKeyManager(obj);
70    }
71
72    @Override
73    public String chooseServerKeyIdentityHint(Socket socket) {
74        try {
75            return (String) mDelegate.getClass()
76                    .getMethod("chooseServerKeyIdentityHint", Socket.class)
77                    .invoke(mDelegate, socket);
78        } catch (Exception e) {
79            throw new RuntimeException("Failed to invoke chooseServerKeyIdentityHint", e);
80        }
81    }
82
83    @Override
84    public String chooseServerKeyIdentityHint(SSLEngine engine) {
85        try {
86            return (String) mDelegate.getClass()
87                    .getMethod("chooseServerKeyIdentityHint", SSLEngine.class)
88                    .invoke(mDelegate, engine);
89        } catch (Exception e) {
90            throw new RuntimeException("Failed to invoke chooseServerKeyIdentityHint", e);
91        }
92    }
93
94    @Override
95    public String chooseClientKeyIdentity(String identityHint, Socket socket) {
96        try {
97            return (String) mDelegate.getClass()
98                    .getMethod("chooseClientKeyIdentity", String.class, Socket.class)
99                    .invoke(mDelegate, identityHint, socket);
100        } catch (Exception e) {
101            throw new RuntimeException("Failed to invoke chooseClientKeyIdentity", e);
102        }
103    }
104
105    @Override
106    public String chooseClientKeyIdentity(String identityHint, SSLEngine engine) {
107        try {
108            return (String) mDelegate.getClass()
109                    .getMethod("chooseClientKeyIdentity", String.class, SSLEngine.class)
110                    .invoke(mDelegate, identityHint, engine);
111        } catch (Exception e) {
112            throw new RuntimeException("Failed to invoke chooseClientKeyIdentity", e);
113        }
114    }
115
116    @Override
117    public SecretKey getKey(String identityHint, String identity, Socket socket) {
118        try {
119            return (SecretKey) mDelegate.getClass()
120                    .getMethod("getKey", String.class, String.class, Socket.class)
121                    .invoke(mDelegate, identityHint, identity, socket);
122        } catch (Exception e) {
123            throw new RuntimeException("Failed to invoke getKey", e);
124        }
125    }
126
127    @Override
128    public SecretKey getKey(String identityHint, String identity, SSLEngine engine) {
129        try {
130            return (SecretKey) mDelegate.getClass()
131                    .getMethod("getKey", String.class, String.class, SSLEngine.class)
132                    .invoke(mDelegate, identityHint, identity, engine);
133        } catch (Exception e) {
134            throw new RuntimeException("Failed to invoke getKey", e);
135        }
136    }
137}
138