1 /*
2  * Copyright (C) 2022 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 
17 package com.android.server.uwb.secure;
18 
19 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.INS_SELECT;
20 import static com.android.server.uwb.secure.iso7816.Iso7816Constants.P1_SELECT_BY_DEDICATED_FILE_NAME;
21 import static com.android.server.uwb.secure.iso7816.StatusWord.SW_NO_ERROR;
22 
23 import android.os.Looper;
24 import android.os.Message;
25 import android.util.Log;
26 
27 import androidx.annotation.NonNull;
28 
29 import com.android.server.uwb.discovery.Transport;
30 import com.android.server.uwb.pm.RunningProfileSessionInfo;
31 import com.android.server.uwb.secure.iso7816.CommandApdu;
32 import com.android.server.uwb.secure.iso7816.ResponseApdu;
33 
34 import java.io.IOException;
35 
36 class ResponderSecureChannel extends FiRaSecureChannel {
37     private static final String LOG_TAG = "ResponderSecureChannel";
38 
ResponderSecureChannel( @onNull SecureElementChannel secureElementChannel, @NonNull Transport transport, @NonNull Looper workLooper, @NonNull RunningProfileSessionInfo runningProfileSessionInfo)39     ResponderSecureChannel(
40             @NonNull SecureElementChannel secureElementChannel,
41             @NonNull Transport transport,
42             @NonNull Looper workLooper,
43             @NonNull RunningProfileSessionInfo runningProfileSessionInfo) {
44         super(secureElementChannel, transport, workLooper, runningProfileSessionInfo);
45     }
46 
47     @Override
handleScMessage(@onNull Message msg)48     protected void handleScMessage(@NonNull Message msg) {
49         switch (msg.what) {
50             case CMD_OPEN_CHANNEL:
51                 try {
52                     ResponseApdu responseApdu = mSecureElementChannel.openChannelWithResponse();
53                     if (responseApdu.getStatusWord() == SW_NO_ERROR.toInt()) {
54                         if (mRunningProfileSessionInfo.secureBlob.isPresent()) {
55                             if (!swapInAdf(
56                                     mRunningProfileSessionInfo.secureBlob.get(),
57                                     mRunningProfileSessionInfo.oidOfProvisionedAdf,
58                                     mRunningProfileSessionInfo.controleeInfo.get().toBytes())) {
59                                 mSecureElementChannel.closeChannel();
60                                 throw new IllegalStateException("Error on swapping in ADF");
61                             }
62                         }
63                         mStatus = Status.CHANNEL_OPENED;
64                     } else {
65                         throw new IllegalStateException(
66                                 String.valueOf(responseApdu.getStatusWord()));
67                     }
68                     mWorkHandler.sendMessage(
69                             mWorkHandler.obtainMessage(
70                                     CMD_SEND_OOB_DATA, OOB_MSG_TYPE_APDU_RESPONSE, 0,
71                                     responseApdu.toByteArray()));
72                 } catch (IOException | IllegalStateException e) {
73                     logw("Error on open channel: " + e);
74                     mSecureChannelCallback.onSetUpError(SetupError.OPEN_SE_CHANNEL);
75                     ResponseApdu responseApdu = ResponseApdu.SW_APPLET_SELECT_FAILED_APDU;
76                     mWorkHandler.sendMessage(
77                             mWorkHandler.obtainMessage(
78                                     CMD_SEND_OOB_DATA, OOB_MSG_TYPE_APDU_RESPONSE, 0,
79                                     responseApdu.toByteArray()));
80                 }
81                 // waiting for next request from the initiator.
82                 break;
83             default:
84                 super.handleScMessage(msg);
85         }
86     }
87 
88     @Override
doOpenSeChannelAfterInit()89     protected boolean doOpenSeChannelAfterInit() {
90         return false;
91     }
92 
93     @Override
preprocessRemoteCommand(@onNull byte[] data)94     protected boolean preprocessRemoteCommand(@NonNull byte[] data) {
95         if (mStatus == Status.INITIALIZED && isSelectApdu(data)) {
96             mWorkHandler.sendMessage(mWorkHandler.obtainMessage(CMD_OPEN_CHANNEL));
97 
98             return true;
99         }
100         return false;
101     }
102 
isSelectApdu(@onNull byte[] data)103     private boolean isSelectApdu(@NonNull byte[] data) {
104         try {
105             CommandApdu command = CommandApdu.parse(data);
106             return command.getCla() == (byte) 0x00
107                     && command.getIns() == INS_SELECT
108                     && command.getP1() == P1_SELECT_BY_DEDICATED_FILE_NAME;
109         } catch (IllegalArgumentException e) {
110             return false;
111         }
112     }
113 
114     @Override
tunnelToRemoteDevice( @onNull byte[] data, @NonNull ExternalRequestCallback externalRequestCallback)115     void tunnelToRemoteDevice(
116             @NonNull byte[] data, @NonNull ExternalRequestCallback externalRequestCallback) {
117         throw new IllegalStateException("tunnel is not supported for the Responder.");
118     }
119 
logw(@onNull String dbgMsg)120     private void logw(@NonNull String dbgMsg) {
121         Log.w(LOG_TAG, dbgMsg);
122     }
123 }
124