| /* |
| * Copyright (C) 2017 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. |
| */ |
| /* |
| * Copyright (c) 2017, The Linux Foundation. |
| */ |
| |
| /* |
| * Copyright 2012 Giesecke & Devrient GmbH. |
| * |
| * 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 com.android.se.security.gpac; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| |
| /** |
| * APDU-AR-DO: An APDU access rule data object defines an access rule for APDU access. The APDU |
| * access can either be restricted by a general rule based on an access is NEVER/ ALWAYS allowed |
| * policy or by a specific rule based on APDU filters which defines the range of allowed APDUs more |
| * precisely. |
| */ |
| public class APDU_AR_DO extends BerTlv { |
| |
| public static final int TAG = 0xD0; |
| |
| private boolean mApduAllowed = false; |
| private ArrayList<byte[]> mApduHeader = new ArrayList<byte[]>(); |
| private ArrayList<byte[]> mFilterMask = new ArrayList<byte[]>(); |
| |
| public APDU_AR_DO(byte[] rawData, int valueIndex, int valueLength) { |
| super(rawData, TAG, valueIndex, valueLength); |
| } |
| |
| public APDU_AR_DO(boolean allowed) { |
| super(null, TAG, 0, 0); |
| mApduAllowed = allowed; |
| } |
| |
| public APDU_AR_DO(ArrayList<byte[]> apduHeader, ArrayList<byte[]> filterMask) { |
| super(null, TAG, 0, 0); |
| mApduHeader = apduHeader; |
| mFilterMask = filterMask; |
| } |
| |
| public boolean isApduAllowed() { |
| return mApduAllowed; |
| } |
| |
| public ArrayList<byte[]> getApduHeaderList() { |
| return mApduHeader; |
| } |
| |
| public ArrayList<byte[]> getFilterMaskList() { |
| return mFilterMask; |
| } |
| |
| @Override |
| /** |
| * Tag: D0 Length: 1 or n*8 1 if value contains a general APDU access rule. n*8 if value |
| * contains |
| * a specific APDU access rule. |
| * |
| * <p>Value: Contains a general APDU access rule: NEVER (00): APDU access is not allowed |
| * ALWAYS(01): APDU access is allowed or contains a specific APDU access rule based on one or |
| * more |
| * APDU filter(s): APDU filter: 8 bytes APDU filter mask consists of: 4 bytes APDU header |
| * (defines |
| * the header of allowed APDUs) 4 bytes APDU mask (bit set defines the bits which shall be |
| * considered for the APDU header comparison) An APDU filter has to be applied as follows: |
| * if((APDUHeader & FilterMask) == FilterAPDUHeader) then allow APDU |
| */ |
| public void interpret() throws ParserException { |
| |
| mApduAllowed = false; |
| mApduHeader.clear(); |
| mFilterMask.clear(); |
| |
| byte[] data = getRawData(); |
| int index = getValueIndex(); |
| |
| if (index + getValueLength() > data.length) { |
| throw new ParserException("Not enough data for APDU_AR_DO!"); |
| } |
| |
| // APDU-AR-DO contains either a flag which allows/disallows APDU communication |
| // or |
| // it contains APDU filter (APDUHeader | FilterMask) which should have length n*8. |
| if (getValueLength() == 1) { |
| if ((data[index] != 0x00) && (data[index] != 0x01)) { |
| // Undefined value cannot be treated as a general APDU access rule. |
| // Access to the SE shall not be allowed when the interpretation error occurs. |
| throw new ParserException("Invalid value of APDU-AR-DO : " + String.format("%02x", |
| data[index] & 0xff)); |
| } |
| mApduAllowed = (data[index] == 0x01); |
| } else if ((getValueLength() % 8 == 0) && (getValueLength() != 0)) { |
| mApduAllowed = true; |
| |
| for (int i = index; i < index + getValueLength(); i += 8) { |
| byte[] apduHeader = new byte[4]; |
| byte[] filterMask = new byte[4]; |
| |
| apduHeader[0] = data[i + 0]; |
| apduHeader[1] = data[i + 1]; |
| apduHeader[2] = data[i + 2]; |
| apduHeader[3] = data[i + 3]; |
| filterMask[0] = data[i + 4]; |
| filterMask[1] = data[i + 5]; |
| filterMask[2] = data[i + 6]; |
| filterMask[3] = data[i + 7]; |
| |
| mApduHeader.add(apduHeader); |
| mFilterMask.add(filterMask); |
| } |
| } else if (getValueLength() == 0) { |
| mApduAllowed = false; |
| } else { |
| throw new ParserException("Invalid length of APDU-AR-DO!"); |
| } |
| } |
| |
| @Override |
| /** |
| * Tag: D0 Length: 1 or n*8 1 if value contains a general APDU access rule. n*8 if value |
| * contains |
| * a specific APDU access rule. |
| * |
| * <p>Value: Contains a general APDU access rule: NEVER (00): APDU access is not allowed |
| * ALWAYS(01): APDU access is allowed or contains a specific APDU access rule based on one or |
| * more |
| * APDU filter(s): APDU filter: 8 bytes APDU filter mask consists of: 4 bytes APDU header |
| * (defines |
| * the header of allowed APDUs) 4 bytes APDU mask (bit set defines the bits which shall be |
| * considered for the APDU header comparison) An APDU filter has to be applied as follows: |
| * if((APDUHeader & FilterMask) == FilterAPDUHeader) then allow APDU |
| */ |
| public void build(ByteArrayOutputStream stream) throws DO_Exception { |
| |
| // APDU header and filter mask has to have the same size |
| // even if they are not used (then size() == 0 ). |
| if (mApduHeader.size() != this.mFilterMask.size()) { |
| throw new DO_Exception("APDU filter is invalid"); |
| } |
| |
| // write tag |
| stream.write(getTag()); |
| |
| // check if APDU Flag shall be written |
| if (mApduHeader.size() == 0) { |
| stream.write(0x01); |
| stream.write(this.mApduAllowed ? 0x01 : 0x00); |
| } else { |
| ByteArrayOutputStream temp = new ByteArrayOutputStream(); |
| for (int i = 0; i < mApduHeader.size(); i++) { |
| byte[] apduHeader = mApduHeader.get(i); |
| byte[] filterMask = mFilterMask.get(i); |
| |
| if (apduHeader.length != 4 || filterMask.length != 4) { |
| throw new DO_Exception("APDU filter is invalid!"); |
| } |
| |
| try { |
| temp.write(apduHeader); |
| temp.write(filterMask); |
| } catch (IOException e) { |
| throw new DO_Exception("APDU Filter Memory IO problem! " + e.getMessage()); |
| } |
| } |
| |
| BerTlv.encodeLength(temp.size(), stream); |
| try { |
| stream.write(temp.toByteArray()); |
| } catch (IOException e) { |
| throw new DO_Exception("APDU Filter Memory IO problem! " + e.getMessage()); |
| } |
| } |
| } |
| } |