package de.persosim.simulator.tlv;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
/**
* This class implements BER-TLV data objects with constructed encoding, i.e.
* TLV data objects that in their data field again may contain an arbitrarily
* large number of TLV data objects.
*
* Tag field: All access to this element must be committed through methods
* provided by this object. If constructed or set the field must not be set
* directly as reference to the provided object but to a clone. Accordingly any
* getter must also return a clone of this field.
*
* This behavior e.g. is to prevent anyone from converting primitive encoding to
* constructed by merely modifying the respective bit.
*
* Length field: References to this object do not need to be protected. When
* asked for its length field the object first checks whether any length field
* has been set at all. If a length field has been set it checks whether its
* encoded value matches the actual length of the value field (This is
* recursively computed solely based on the actual length of any (sub-)
* element). If so, the length field is returned as is. Otherwise the default
* DER encoding of the actual length of the value field is returned. In case an
* invalid length has been set on purpose, checks can explicitly be disabled.
* This is flagged within the object. If a flagged object is asked for its
* length field, it returns the set length field without any checks. If no
* length field is set, it will be computed and returned as if the flag had not
* been set.
*
* This behavior saves costs for strict access control on length or value
* fields.
*
* Value field: Analogous to the length field, the value field may also be
* accessed freely. All problems that may arise from this are dealt by the way
* the length field is determined. Considering the possible complexity of
* constructed TLV data objects, the costs for ensuring restrictive access
* control would exceed any benefit from it.
*
* @author slutters
*
*/
public class ConstructedTlvDataObject extends TlvDataObject implements TlvDataStructure {
protected TlvDataObjectContainer tlvDataObjectContainer;
/*--------------------------------------------------------------------------------*/
/**
* Constructor for a TLV data object with constructed encoding based on a range
* from an array of raw bytes.
* The defined range must contain at least the whole TLV data object.
* The first byte of the range must also be the first byte of the TLV data object.
* The last byte of the range is not expected to also be the last byte of the TLV data
* object and may lay outside of the array.
*
* @param byteArray the array that contains the TLV data object
* @param minOffset the first offset of the range to contain the TLV data object (inclusive)
* @param maxOffset the first offset of the range to not contain the TLV data object (exclusive).
* This offset may no longer be part of the array.
*/
public ConstructedTlvDataObject(byte[] byteArray, int minOffset, int maxOffset) {
super(byteArray, minOffset, maxOffset);
if(!tlvTag.indicatesEncodingConstructed()) {throw new IllegalArgumentException("tag must be constructed");}
int minOffsetSub = minOffset + tlvTag.getLength() + tlvLength.getLength();
int maxOffsetSub = (minOffsetSub + tlvLength.getIndicatedLength());
tlvDataObjectContainer = new TlvDataObjectContainer(byteArray, minOffsetSub, maxOffsetSub);
}
/**
* Constructor for a TLV data object with constructed encoding based on a fixed array of raw bytes.
* The defined byte array must contain exactly the whole TLV data object.
*
* @param byteArray the array that contains the TLV data object
*/
public ConstructedTlvDataObject(byte[] byteArray) {
this(byteArray, 0, byteArray.length);
if(this.getLength() != byteArray.length) {
throw new IllegalArgumentException("input data is longer than actual TLV data object");
}
}
/**
* Constructs an object from pre-fabricated elements explicitly setting a length field.
* Length fields only need to be explicitly set if their encoding complies with BER but
* not DER encoding rules or their encoding is intentionally damaged.
* @param tlvTagInput the tag to be used
* @param tlvLengthInput the length to be used
* @param tlvDataObjectContainerInput the data to be used
* @param performValidityChecksInput true: perform validity checks, false: do not perform validity checks
*/
public ConstructedTlvDataObject(TlvTag tlvTagInput, TlvLength tlvLengthInput, TlvDataObjectContainer tlvDataObjectContainerInput, boolean performValidityChecksInput) {
super(performValidityChecksInput);
this.setTag(tlvTagInput, performValidityChecksInput);
this.setValue(tlvDataObjectContainerInput);
if(tlvLengthInput != null) {
/* length must be set last as its validity check depends on the value already being set */
this.setLength(tlvLengthInput, performValidityChecksInput);
}
}
/**
* Constructs an object from pre-fabricated elements explicitly setting a length field.
* Length fields only need to be explicitly set if their encoding complies with BER but
* not DER encoding rules or their encoding is intentionally damaged.
* @param tlvTagInput the tag to be used
* @param tlvLengthInput the length to be used
* @param tlvDataObjectContainerInput the data to be used
*/
public ConstructedTlvDataObject(TlvTag tlvTagInput, TlvLength tlvLengthInput, TlvDataObjectContainer tlvDataObjectContainerInput) {
this(tlvTagInput, tlvLengthInput, tlvDataObjectContainerInput, PERFORM_VALIDITY_CHECKS);
}
/**
* Constructs an object from pre-fabricated elements. Length field is implicitly set
* according to DER encoding rules by default.
* @param tlvTagInput the tag to be used
* @param tlvDataObjectContainerInput the value to be used
* @param performValidityChecksInput true: perform validity checks, false: do not perform validity checks
*/
public ConstructedTlvDataObject(TlvTag tlvTagInput, TlvDataObjectContainer tlvDataObjectContainerInput, boolean performValidityChecksInput) {
this(tlvTagInput, null, tlvDataObjectContainerInput, performValidityChecksInput);
}
/**
* Constructs an object from pre-fabricated elements. Length field is implicitly set
* according to DER encoding rules by default.
* @param tlvTagInput the tag to be used
* @param tlvDataObjectContainerInput the value to be used
*/
public ConstructedTlvDataObject(TlvTag tlvTagInput, TlvDataObjectContainer tlvDataObjectContainerInput) {
this(tlvTagInput, null, tlvDataObjectContainerInput, PERFORM_VALIDITY_CHECKS);
}
/**
* Constructs an object from pre-fabricated elements. Length field is implicitly set
* according to DER encoding rules by default.
* @param tlvTagInput the tag to be used
* @param tlvDataObjectInputs the value elemets to be used
*/
public ConstructedTlvDataObject(TlvTag tlvTagInput, TlvDataObject... tlvDataObjectInputs) {
this(tlvTagInput, new TlvDataObjectContainer(tlvDataObjectInputs));
}
/**
* Constructs an object from pre-fabricated elements. Length field is implicitly set
* according to DER encoding rules by default.
* @param tlvTagInput the tag to be used
*/
public ConstructedTlvDataObject(TlvTag tlvTagInput) {
this(tlvTagInput, new TlvDataObjectContainer());
}
/*--------------------------------------------------------------------------------*/
@Override
public void setTag(TlvTag tlvTagInput, boolean performValidityChecksInput) {
if(tlvTagInput == null) {throw new NullPointerException("tag must not be null");}
if(!tlvTagInput.indicatesEncodingConstructed()) {throw new IllegalArgumentException("tag must be constructed");}
performValidityChecks = performValidityChecksInput;
if(performValidityChecks) {
if(!tlvTagInput.isValidBerEncoding()) {throw new IllegalArgumentException("tag must be valid BER encoding");};
}
/*
* TLV tag must be cloned to eliminate outside access to this object.
* The tag must only be set by methods offered by this class e.g. to
* prevent setting the primitive tag to be a constructed tag.
*/
tlvTag = tlvTagInput.clone();
}
/**
* @return the tlvDataObjectContainer
*/
public TlvDataObjectContainer getTlvDataObjectContainer() {
return tlvDataObjectContainer;
}
/*--------------------------------------------------------------------------------*/
/*------------------------- Inherited from TLVDataStructure ----------------------*/
/*--------------------------------------------------------------------------------*/
@Override
public TlvDataObject getTlvDataObject(TlvPath path) {
return tlvDataObjectContainer.getTlvDataObject(path);
}
@Override
public TlvDataObject getTlvDataObject(TlvTag tlvTag) {
return tlvDataObjectContainer.getTlvDataObject(tlvTag);
}
@Override
public TlvDataObject getTlvDataObject(TlvTagIdentifier tagIdentifier) {
return tlvDataObjectContainer.getTlvDataObject(tagIdentifier);
}
@Override
public TlvDataObject getTlvDataObject(TlvPath path, int index) {
return tlvDataObjectContainer.getTlvDataObject(path, index);
}
@Override
public boolean containsTlvDataObject(TlvTag tagField) {
return tlvDataObjectContainer.containsTlvDataObject(tagField);
}
/*--------------------------------------------------------------------------------*/
@Override
public void addTlvDataObject(TlvPath path, TlvDataObject tlvDataObject) {
tlvDataObjectContainer.addTlvDataObject(path, tlvDataObject);
}
@Override
public void addTlvDataObject(TlvDataObject... tlvDataObject) {
tlvDataObjectContainer.addTlvDataObject(tlvDataObject);
}
@Override
public void removeTlvDataObject(TlvPath path) {
tlvDataObjectContainer.removeTlvDataObject(path);
}
@Override
public void removeTlvDataObject(TlvTagIdentifier tagIdentifier) {
tlvDataObjectContainer.removeTlvDataObject(tagIdentifier);
}
@Override
public void removeTlvDataObject(TlvTag tlvTag) {
tlvDataObjectContainer.removeTlvDataObject(tlvTag);
}
@Override
public void sort(Comparator<TlvDataObject> comparator) {
tlvDataObjectContainer.sort(comparator);
}
/*--------------------------------------------------------------------------------*/
@Override
public int getNoOfElements(boolean recursive) {
return tlvDataObjectContainer.getNoOfElements(recursive);
}
@Override
public int getNoOfElements() {
return tlvDataObjectContainer.getNoOfElements();
}
/*--------------------------------------------------------------------------------*/
/*------------------------- Inherited from TLVDataObject -------------------------*/
/*--------------------------------------------------------------------------------*/
@Override
public int getNoOfValueBytes() {
return tlvDataObjectContainer.getLength();
}
@Override
public byte[] getValueField() {
return tlvDataObjectContainer.toByteArray();
}
/*--------------------------------------------------------------------------------*/
@Override
public Iterator<TlvDataObject> iterator() {
return tlvDataObjectContainer.iterator();
}
/*--------------------------------------------------------------------------------*/
/**
* Sets the value field of this object.
* Method does not override a super class method due to different signatures required for this method
* in both subclasses for primitive and constructed encoding.
* @param tlvDataObjectContainerInput the value to be set
*/
public void setValue(TlvDataObjectContainer tlvDataObjectContainerInput) {
if(tlvDataObjectContainerInput == null) {throw new NullPointerException("value must not be null");}
tlvDataObjectContainer = (TlvDataObjectContainer) tlvDataObjectContainerInput;
}
@Override
public TlvValue getTlvValue() {
return tlvDataObjectContainer;
}
@Override
public boolean isValidBerEncoding() {
if(!super.isValidBerEncoding()) {return false;}
return tlvTag.indicatesEncodingConstructed();
}
/**
* Add all provided {@link TlvDataObject}s to this container.
* @param newTlvDataObjects
*/
public void addAll(Collection<? extends TlvDataObject> newTlvDataObjects) {
for (TlvDataObject curTlvDataObject : newTlvDataObjects) {
addTlvDataObject(curTlvDataObject);
}
}
}