blob: 966a4423b418465fd23e3818e335a488e187be7d [file] [log] [blame]
/*
* 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.util.Arrays;
/** Base Class for all Access Control Data Objects */
public class BerTlv {
private byte[] mRawData = new byte[0];
private int mTag = 0;
private int mValueIndex = 0;
private int mValueLength = 0;
public BerTlv(byte[] rawData, int tag, int valueIndex, int valueLength) {
if (rawData != null) mRawData = rawData;
mTag = tag;
mValueIndex = valueIndex;
mValueLength = valueLength;
}
/** Converts the byte into Hex */
public static String toHex(byte[] digest) {
String digits = "0123456789abcdef";
StringBuilder sb = new StringBuilder(digest.length * 2);
for (byte b : digest) {
int bi = b & 0xff;
sb.append(digits.charAt(bi >> 4));
sb.append(digits.charAt(bi & 0xf));
}
return sb.toString();
}
/** Decodes the byte array into BerTlv Object. Performs checks for length and tag */
public static BerTlv decode(byte[] data, int startIndex) throws ParserException {
return BerTlv.decode(data, startIndex, true);
}
/** Decodes the byte array into BerTlv Object. Performs checks for length and tag */
public static BerTlv decode(byte[] data, int startIndex, boolean containsAllData)
throws ParserException {
if (data == null || data.length == 0) {
throw new ParserException("No data given!");
}
int curIndex = startIndex;
int tag = 0;
/* tag */
if (curIndex < data.length) {
int temp = data[curIndex++] & 0xff;
switch (temp) {
case 0xff: // tag is in two byte format
case 0xdf:
if (curIndex < data.length) {
tag = ((temp & 0xff) << 8) | (data[curIndex++] & 0xff);
} else {
throw new ParserException(
"Index " + curIndex + " out of range! [0..[" + data.length);
}
break;
default: // tag is in single-byte format
tag = temp;
break;
}
} else {
throw new ParserException("Index " + curIndex + " out of range! [0..[" + data.length);
}
/* length */
int length;
if (curIndex < data.length) {
int temp = data[curIndex++] & 0xff;
if (temp < 0x80) {
length = temp;
} else if (temp == 0x81) {
if (curIndex < data.length) {
length = data[curIndex++] & 0xff;
if (length < 0x80) {
throw new ParserException("Invalid TLV length encoding!");
}
if (containsAllData && data.length < length + curIndex) {
throw new ParserException("Not enough data provided!");
}
} else {
throw new ParserException(
"Index " + curIndex + " out of range! [0..[" + data.length);
}
} else if (temp == 0x82) {
if ((curIndex + 1) < data.length) {
length = ((data[curIndex] & 0xff) << 8) | (data[curIndex + 1] & 0xff);
} else {
throw new ParserException("Index out of range! [0..[" + data.length);
}
curIndex += 2;
if (length < 0x100) {
throw new ParserException("Invalid TLV length encoding!");
}
if (containsAllData && data.length < length + curIndex) {
throw new ParserException("Not enough data provided!");
}
} else if (temp == 0x83) {
if ((curIndex + 2) < data.length) {
length =
((data[curIndex] & 0xff) << 16)
| ((data[curIndex + 1] & 0xff) << 8)
| (data[curIndex + 2] & 0xff);
} else {
throw new ParserException("Index out of range! [0..[" + data.length);
}
curIndex += 3;
if (length < 0x10000) {
throw new ParserException("Invalid TLV length encoding!");
}
if (containsAllData && data.length < length + curIndex) {
throw new ParserException("Not enough data provided!");
}
} else {
throw new ParserException("Unsupported TLV length encoding!");
}
} else {
throw new ParserException("Index " + curIndex + " out of range! [0..[" + data.length);
}
// create object
return new BerTlv(data, tag, curIndex, length);
}
/**
* Encodes length according to ASN1. Supported are length values up to 3 bytes -> 83 xx yy zz.
*/
public static void encodeLength(int length, ByteArrayOutputStream stream) {
if (length > 0x0000FFFF) {
stream.write(0x83);
stream.write(((length & 0x00FF0000) >> 16));
stream.write(((length & 0x0000FF00) >> 8));
stream.write((length & 0x000000FF));
} else if (length > 0x000000FF) {
stream.write(0x82);
stream.write(((length & 0x0000FF00) >> 8));
stream.write((length & 0x000000FF));
} else if (length > 0x0000007F) {
stream.write(0x81);
stream.write((length & 0x000000FF));
} else {
stream.write((length & 0x000000FF));
}
}
/**
* Interprets the byte stream and performs the required checks.
* To to overridden in the derived class.
*/
public void interpret() throws ParserException {
}
/**
* Builds up the TLV into a byte stream.
*
* <p>Tags can be encoded as one or two bytes
*/
public void build(ByteArrayOutputStream stream) throws DO_Exception {
// put tag into stream
if (mTag > 0xFF) {
stream.write(((mTag & 0x0000FF00) >> 8));
stream.write((mTag & 0x000000FF));
} else {
stream.write((mTag & 0x000000FF));
}
// write length
encodeLength(mValueLength, stream);
// write value
if (mValueLength > 0) {
stream.write(mRawData, mValueIndex, mValueLength);
}
}
public int getTag() {
return mTag;
}
public int getValueIndex() {
return mValueIndex;
}
/** Returns the byte array of only the data values */
public byte[] getValue() {
// quick checks
if (mRawData == null
|| mValueLength == 0
|| mValueIndex < 0
|| mValueIndex > mRawData.length
|| mValueIndex + mValueLength > mRawData.length) {
return new byte[0];
}
byte[] data = new byte[mValueLength];
System.arraycopy(mRawData, mValueIndex, data, 0, mValueLength);
return data;
}
protected byte[] getRawData() {
return mRawData;
}
public int getValueLength() {
return mValueLength;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BerTlv) {
BerTlv berTlv = (BerTlv) obj;
if (mTag == berTlv.mTag) {
byte[] test1 = this.getValue();
byte[] test2 = berTlv.getValue();
return Arrays.equals(test1, test2);
}
}
return false;
}
}