1 /*
2  * Copyright (C) 2017 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  * Copyright (c) 2017, The Linux Foundation.
18  */
19 
20 /*
21  * Copyright 2012 Giesecke & Devrient GmbH.
22  *
23  * Licensed under the Apache License, Version 2.0 (the "License");
24  * you may not use this file except in compliance with the License.
25  * You may obtain a copy of the License at
26  *
27  *      http://www.apache.org/licenses/LICENSE-2.0
28  *
29  * Unless required by applicable law or agreed to in writing, software
30  * distributed under the License is distributed on an "AS IS" BASIS,
31  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32  * See the License for the specific language governing permissions and
33  * limitations under the License.
34  */
35 package com.android.se.security.gpac;
36 
37 import java.io.ByteArrayOutputStream;
38 import java.io.IOException;
39 import java.util.ArrayList;
40 
41 /**
42  * APDU-AR-DO: An APDU access rule data object defines an access rule for APDU access. The APDU
43  * access can either be restricted by a general rule based on an access is NEVER/ ALWAYS allowed
44  * policy or by a specific rule based on APDU filters which defines the range of allowed APDUs more
45  * precisely.
46  */
47 public class APDU_AR_DO extends BerTlv {
48 
49     public static final int TAG = 0xD0;
50 
51     private boolean mApduAllowed = false;
52     private ArrayList<byte[]> mApduHeader = new ArrayList<byte[]>();
53     private ArrayList<byte[]> mFilterMask = new ArrayList<byte[]>();
54 
APDU_AR_DO(byte[] rawData, int valueIndex, int valueLength)55     public APDU_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
56         super(rawData, TAG, valueIndex, valueLength);
57     }
58 
APDU_AR_DO(boolean allowed)59     public APDU_AR_DO(boolean allowed) {
60         super(null, TAG, 0, 0);
61         mApduAllowed = allowed;
62     }
63 
APDU_AR_DO(ArrayList<byte[]> apduHeader, ArrayList<byte[]> filterMask)64     public APDU_AR_DO(ArrayList<byte[]> apduHeader, ArrayList<byte[]> filterMask) {
65         super(null, TAG, 0, 0);
66         mApduHeader = apduHeader;
67         mFilterMask = filterMask;
68     }
69 
isApduAllowed()70     public boolean isApduAllowed() {
71         return mApduAllowed;
72     }
73 
getApduHeaderList()74     public ArrayList<byte[]> getApduHeaderList() {
75         return mApduHeader;
76     }
77 
getFilterMaskList()78     public ArrayList<byte[]> getFilterMaskList() {
79         return mFilterMask;
80     }
81 
82     @Override
83     /**
84      * Tag: D0 Length: 1 or n*8 1 if value contains a general APDU access rule. n*8 if value
85      * contains
86      * a specific APDU access rule.
87      *
88      * <p>Value: Contains a general APDU access rule: NEVER (00): APDU access is not allowed
89      * ALWAYS(01): APDU access is allowed or contains a specific APDU access rule based on one or
90      * more
91      * APDU filter(s): APDU filter: 8 bytes APDU filter mask consists of: 4 bytes APDU header
92      * (defines
93      * the header of allowed APDUs) 4 bytes APDU mask (bit set defines the bits which shall be
94      * considered for the APDU header comparison) An APDU filter has to be applied as follows:
95      * if((APDUHeader & FilterMask) == FilterAPDUHeader) then allow APDU
96      */
interpret()97     public void interpret() throws ParserException {
98 
99         mApduAllowed = false;
100         mApduHeader.clear();
101         mFilterMask.clear();
102 
103         byte[] data = getRawData();
104         int index = getValueIndex();
105 
106         if (index + getValueLength() > data.length) {
107             throw new ParserException("Not enough data for APDU_AR_DO!");
108         }
109 
110         // APDU-AR-DO contains either a flag which allows/disallows APDU communication
111         // or
112         // it contains APDU filter (APDUHeader | FilterMask) which should have length n*8.
113         if (getValueLength() == 1) {
114             if ((data[index] != 0x00) && (data[index] != 0x01)) {
115                 // Undefined value cannot be treated as a general APDU access rule.
116                 // Access to the SE shall not be allowed when the interpretation error occurs.
117                 throw new ParserException("Invalid value of APDU-AR-DO : " + String.format("%02x",
118                         data[index] & 0xff));
119             }
120             mApduAllowed = (data[index] == 0x01);
121         } else if ((getValueLength() % 8 == 0) && (getValueLength() != 0)) {
122             mApduAllowed = true;
123 
124             for (int i = index; i < index + getValueLength(); i += 8) {
125                 byte[] apduHeader = new byte[4];
126                 byte[] filterMask = new byte[4];
127 
128                 apduHeader[0] = data[i + 0];
129                 apduHeader[1] = data[i + 1];
130                 apduHeader[2] = data[i + 2];
131                 apduHeader[3] = data[i + 3];
132                 filterMask[0] = data[i + 4];
133                 filterMask[1] = data[i + 5];
134                 filterMask[2] = data[i + 6];
135                 filterMask[3] = data[i + 7];
136 
137                 mApduHeader.add(apduHeader);
138                 mFilterMask.add(filterMask);
139             }
140         } else if (getValueLength() == 0) {
141             mApduAllowed = false;
142         } else {
143             throw new ParserException("Invalid length of APDU-AR-DO!");
144         }
145     }
146 
147     @Override
148     /**
149      * Tag: D0 Length: 1 or n*8 1 if value contains a general APDU access rule. n*8 if value
150      * contains
151      * a specific APDU access rule.
152      *
153      * <p>Value: Contains a general APDU access rule: NEVER (00): APDU access is not allowed
154      * ALWAYS(01): APDU access is allowed or contains a specific APDU access rule based on one or
155      * more
156      * APDU filter(s): APDU filter: 8 bytes APDU filter mask consists of: 4 bytes APDU header
157      * (defines
158      * the header of allowed APDUs) 4 bytes APDU mask (bit set defines the bits which shall be
159      * considered for the APDU header comparison) An APDU filter has to be applied as follows:
160      * if((APDUHeader & FilterMask) == FilterAPDUHeader) then allow APDU
161      */
build(ByteArrayOutputStream stream)162     public void build(ByteArrayOutputStream stream) throws DO_Exception {
163 
164         // APDU header and filter mask has to have the same size
165         // even if they are not used (then size() == 0 ).
166         if (mApduHeader.size() != this.mFilterMask.size()) {
167             throw new DO_Exception("APDU filter is invalid");
168         }
169 
170         // write tag
171         stream.write(getTag());
172 
173         // check if APDU Flag shall be written
174         if (mApduHeader.size() == 0) {
175             stream.write(0x01);
176             stream.write(this.mApduAllowed ? 0x01 : 0x00);
177         } else {
178             ByteArrayOutputStream temp = new ByteArrayOutputStream();
179             for (int i = 0; i < mApduHeader.size(); i++) {
180                 byte[] apduHeader = mApduHeader.get(i);
181                 byte[] filterMask = mFilterMask.get(i);
182 
183                 if (apduHeader.length != 4 || filterMask.length != 4) {
184                     throw new DO_Exception("APDU filter is invalid!");
185                 }
186 
187                 try {
188                     temp.write(apduHeader);
189                     temp.write(filterMask);
190                 } catch (IOException e) {
191                     throw new DO_Exception("APDU Filter Memory IO problem! " + e.getMessage());
192                 }
193             }
194 
195             BerTlv.encodeLength(temp.size(), stream);
196             try {
197                 stream.write(temp.toByteArray());
198             } catch (IOException e) {
199                 throw new DO_Exception("APDU Filter Memory IO problem! " + e.getMessage());
200             }
201         }
202     }
203 }
204