/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.common.tlv;
import org.openecard.common.util.ByteUtils;
import org.openecard.common.util.LongUtils;
/**
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class Tag {
///
/// Builtin standard tags
///
public static final Tag BOOLEAN_TAG = new Tag(TagClass.UNIVERSAL, true, 1);
public static final Tag INTEGER_TAG = new Tag(TagClass.UNIVERSAL, true, 2);
public static final Tag BITSTRING_TAG = new Tag(TagClass.UNIVERSAL, true, 3); // primitive or constructed
public static final Tag OCTETSTRING_TAG = new Tag(TagClass.UNIVERSAL, true, 4); // primitive or constructed
public static final Tag NULL_TAG = new Tag(TagClass.UNIVERSAL, true, 5);
public static final Tag OID_TAG = new Tag(TagClass.UNIVERSAL, true, 6);
//public static final Tag ObjectDescriptorTag = new Tag(TagClass.UNIVERSAL, true, 7);
//public static final Tag ExternalTypeTag = new Tag(TagClass.UNIVERSAL, true, 8);
public static final Tag REAL_TAG = new Tag(TagClass.UNIVERSAL, true, 9);
public static final Tag ENUMERATED_TAG = new Tag(TagClass.UNIVERSAL, true, 10);
public static final Tag EMBEDDED_PDV_TAG = new Tag(TagClass.UNIVERSAL, false, 11);
//public static final Tag UTF8Tag = new Tag(TagClass.UNIVERSAL, true, 12);
public static final Tag RELATIVE_OID_TAG = new Tag(TagClass.UNIVERSAL, true, 13);
//public static final Tag TimeTag = new Tag(TagClass.UNIVERSAL, true, 14);
// 15 is reserved for future recommendations
public static final Tag SEQUENCE_TAG = new Tag(TagClass.UNIVERSAL, false, 16);
public static final Tag SET_TAG = new Tag(TagClass.UNIVERSAL, false, 17);
// char strings 18-22, 25-30
//public static final Tag UTCTimeTag = new Tag(TagClass.UNIVERSAL, true, 23);
//public static final Tag GeneralizedTimeTag = new Tag(TagClass.UNIVERSAL, true, 24);
//public static final Tag DateTag = new Tag(TagClass.UNIVERSAL, true, 31);
//public static final Tag TimeOfDayTag = new Tag(TagClass.UNIVERSAL, true, 32);
//public static final Tag DateTimeTag = new Tag(TagClass.UNIVERSAL, true, 33);
//public static final Tag DurationTag = new Tag(TagClass.UNIVERSAL, true, 34);
//public static final Tag IntOIDTag = new Tag(TagClass.UNIVERSAL, true, 35);
//public static final Tag IntOIDReferenceTag = new Tag(TagClass.UNIVERSAL, true, 36);
private TagClass tagClass;
private boolean primitive;
private long tagNum;
private long tagNumWithClass;
/** Set when created from BER. This is needed when creating a TagLengthValue instance from BER */
protected int numOctets=0;
public Tag() {
this(TagClass.UNIVERSAL, true, 0);
}
public Tag(TagClass tagClass, boolean primitive, long tagNum) {
this.tagClass = tagClass;
this.primitive = primitive;
this.tagNum = tagNum;
calculateTagNumWithClass();
}
public TagClass getTagClass() {
return tagClass;
}
public void setTagClass(TagClass tagClass) {
this.tagClass = tagClass;
calculateTagNumWithClass();
}
public boolean isPrimitive() {
return this.primitive;
}
void setPrimitive(boolean primitive) {
this.primitive = primitive;
calculateTagNumWithClass();
}
public long getTagNum() {
return this.tagNum;
}
public void setTagNum(long tagNum) {
this.tagNum = tagNum;
calculateTagNumWithClass();
}
public long getTagNumWithClass() {
return this.tagNumWithClass;
}
public void setTagNumWithClass(long tagNumWithClass) throws TLVException {
Tag newTag = Tag.fromBER(LongUtils.toByteArray(tagNumWithClass));
this.tagClass = newTag.tagClass;
this.primitive = newTag.primitive;
this.tagNum = newTag.tagNum;
this.tagNumWithClass = tagNumWithClass;
}
private void calculateTagNumWithClass() {
byte leading = this.tagClass.num;
leading = (byte) ((leading << 1) | ((this.primitive) ? 0 : 1));
byte[] rest;
if (this.tagNum >= 31) {
// long
leading = (byte) ((leading << 5) | 0x1F);
rest = LongUtils.toByteArray(this.tagNum, 7);
for (int i=0; i < rest.length-1; i++) {
rest[i] |= 0x80;
}
} else {
// short
leading = (byte) ((leading << 5) | this.tagNum);
rest = new byte[0];
}
byte[] resultBytes = ByteUtils.concatenate(leading, rest);
this.tagNumWithClass = ByteUtils.toLong(resultBytes);
}
public static Tag fromBER(byte[] data) throws TLVException {
// how many octets made up this tag?
int numOctets = 1;
// get common values independed from encoding type
TagClass tagClass = TagClass.getTagClass(data[0]);
boolean primitive = ((data[0] >> 5) & 0x01) == 0x00;
// get value so it can be seen if short or long form is present
long tagNum = 0;
byte tmpTagNum = (byte) (data[0] & 0x1F);
if (tmpTagNum <= 30) {
// short form
tagNum = tmpTagNum;
} else {
// long form
byte next;
// read as long as it is not the last octet
do {
// terminate if there are no bytes left or the number is larger than 64 bits
if (numOctets*7 > 64) {
throw new TLVException("Tag number doesn't fit into a 64 bit word.");
} else if (data.length < numOctets) {
throw new TLVException("Not enough bytes in input bytes to build TLV tag.");
}
// get next number
next = data[numOctets];
numOctets++;
// get next bytes and merge them into result
byte nextValue = (byte) (next & 0x7F);
tagNum = (tagNum << 7) | nextValue;
} while (((next >> 7) & 0x01) == 0x01);
}
Tag resultTag = new Tag(tagClass, primitive, tagNum);
resultTag.numOctets = numOctets;
return resultTag;
}
public byte[] toBER() {
return LongUtils.toByteArray(tagNumWithClass);
}
@Override
public String toString() {
return "[" + tagClass.toString() + " " +
(primitive ? "prim " : "cons ") +
tagNum + " (0x" + Long.toHexString(tagNumWithClass) + ")]";
}
}