diff --git a/klvdata/elementparser.py b/klvdata/elementparser.py index 0ff48f2..ee51caf 100755 --- a/klvdata/elementparser.py +++ b/klvdata/elementparser.py @@ -35,7 +35,7 @@ from klvdata.common import float_to_bytes from klvdata.common import str_to_bytes from klvdata.common import ieee754_bytes_to_fp - + class ElementParser(Element, metaclass=ABCMeta): @@ -94,6 +94,29 @@ def __str__(self): return bytes_to_hexstr(self.value, start='0x', sep='') +class EnumElementParser(ElementParser, metaclass=ABCMeta): + def __init__(self, value): + super().__init__(EnumValue(value, self.enumMap)) + + @property + @classmethod + @abstractmethod + def enumMap(cls): + pass + + +class EnumValue(BaseValue): + def __init__(self, value, enumMap): + self.rawValue = value + self.value = enumMap.get(value, f"??? ({self.__bytes__()})") + + def __bytes__(self): + return bytes(self.rawValue) + + def __str__(self): + return self.value + + class DateTimeElementParser(ElementParser, metaclass=ABCMeta): def __init__(self, value): super().__init__(DateTimeValue(value)) diff --git a/klvdata/klvparser.py b/klvdata/klvparser.py index 08062d2..26e3809 100755 --- a/klvdata/klvparser.py +++ b/klvdata/klvparser.py @@ -25,7 +25,7 @@ from io import BytesIO from io import IOBase -from klvdata.common import bytes_to_int +from klvdata.common import bytes_to_int, int_to_bytes class KLVParser(object): @@ -42,7 +42,24 @@ def __iter__(self): return self def __next__(self): - key = self.__read(self.key_length) + # A length of None means the key is a variable length BER OID + if self.key_length is None: + oid = 0 + byte = bytes_to_int(self.__read(1)) + # We limit ourselves to four (4) bytes to prevent infinite loops. + for _ in range(4): + isFinal = byte < 128 + oid = (oid << 7) + (byte & 0x7F) + if (isFinal): + break + byte = bytes_to_int(self.__read(1)) + else: + # We've haven't finished reading the key, so any subsequent reads will be invalid. + raise StopIteration + + key = int_to_bytes(oid) + else: + key = self.__read(self.key_length) byte_length = bytes_to_int(self.__read(1)) diff --git a/klvdata/misb0102.py b/klvdata/misb0102.py index 20895e5..542aaa5 100755 --- a/klvdata/misb0102.py +++ b/klvdata/misb0102.py @@ -23,10 +23,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +from klvdata.common import hexstr_to_bytes from klvdata.element import UnknownElement -from klvdata.elementparser import BytesElementParser +from klvdata.elementparser import MappedElementParser +from klvdata.elementparser import EnumElementParser +from klvdata.elementparser import StringElementParser from klvdata.misb0601 import UASLocalMetadataSet from klvdata.setparser import SetParser +from klvdata.streamparser import StreamParser _classifying_country_coding = { b'\x01': 'ISO-3166 Two Letter', @@ -69,7 +73,17 @@ class UnknownElement(UnknownElement): - pass + @property + def LDSName(self): + return "?" + + @property + def ESDName(self): + return "?" + + @property + def UDSName(self): + return "?" @UASLocalMetadataSet.add_parser @@ -83,14 +97,21 @@ class SecurityLocalMetadataSet(SetParser): Must be a subclass of Element or duck type Element. """ key, name = b'\x30', "Security Local Metadata Set" - key_length = 1 + key_length = None # A length of None means the key is a variable length BER OID + + TAG = 48 + UDSKey = hexstr_to_bytes('06 0E 2B 34 - 02 03 01 01 - 0E 01 03 03 - 02 00 00 00') + LDSName = "Security Local Metadata Set" + ESDName = "" + UDSName = "" + parsers = {} _unknown_element = UnknownElement @SecurityLocalMetadataSet.add_parser -class SecurityClassification(BytesElementParser): +class SecurityClassification(EnumElementParser): """MISB ST0102 Security Classification value interpretation parser. The Security Classification metadata element contains a value @@ -99,10 +120,218 @@ class SecurityClassification(BytesElementParser): """ key = b'\x01' - _classification = { + TAG = 1 + UDSKey = "-" + LDSName = "Security Classification" + ESDName = "" + UDSName = "" + + enumMap = { b'\x01': 'UNCLASSIFIED', b'\x02': 'RESTRICTED', b'\x03': 'CONFIDENTIAL', b'\x04': 'SECRET', b'\x05': 'TOP SECRET', } + + +@SecurityLocalMetadataSet.add_parser +class ClassifyingCountryAndReleasingInstructionCCM(EnumElementParser): + """ + """ + key = b'\x02' + TAG = 2 + UDSKey = "-" + LDSName = "Classifying Country And Releasing Instruction Country Coding Method" + ESDName = "" + UDSName = "" + + enumMap = _classifying_country_coding + + +@SecurityLocalMetadataSet.add_parser +class ClassifyingCountry(StringElementParser): + """ + """ + key = b'\x03' + TAG = 3 + UDSKey = "-" + LDSName = "Classifying Country" + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class SecuritySCISHIInformation(StringElementParser): + """ + """ + key = b'\x04' + TAG = 4 + UDSKey = "-" + LDSName = 'Security-SCI/SHI Information' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class Caveats(StringElementParser): + """ + """ + key = b'\x05' + TAG = 5 + UDSKey = "-" + LDSName = 'Caveats' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class ReleasingInstructions(StringElementParser): + """ + """ + key = b'\x06' + TAG = 6 + UDSKey = "-" + LDSName = 'Releasing Instructions' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class ClassifiedBy(StringElementParser): + """ + """ + key = b'\x07' + TAG = 7 + UDSKey = "-" + LDSName = 'Classified By' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class DerivedFrom(StringElementParser): + """ + """ + key = b'\x08' + TAG = 8 + UDSKey = "-" + LDSName = 'Derived From' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class ClassificationReason(StringElementParser): + """ + """ + key = b'\x09' + TAG = 9 + UDSKey = "-" + LDSName = 'Classification Reason' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class DeclassificationDate(StringElementParser): + """ + """ + key = b'\x0A' + TAG = 10 + UDSKey = "-" + LDSName = 'Declassification Date' + ESDName = "" + UDSName = "" + min_length, max_length = 8, 8 + + +@SecurityLocalMetadataSet.add_parser +class ClassificationAndMarkingSystem(StringElementParser): + """ + """ + key = b'\x0B' + TAG = 11 + UDSKey = "-" + LDSName = 'Classification And Marking System' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class ObjectCountryCodingMethod(EnumElementParser): + """ + """ + key = b'\x0C' + TAG = 12 + UDSKey = "-" + LDSName = 'Object Country Coding Method' + ESDName = "" + UDSName = "" + + enumMap = _object_country_coding + + +@SecurityLocalMetadataSet.add_parser +class ObjectCountryCodes(StringElementParser): + """ + """ + key = b'\x0D' + TAG = 13 + UDSKey = "-" + LDSName = 'Object Country Codes' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class ClassificationComments(StringElementParser): + """ + """ + key = b'\x0E' + TAG = 14 + UDSKey = "-" + LDSName = 'Classification Comments' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class Version(MappedElementParser): + """ + """ + key = b'\x16' + TAG = 22 + UDSKey = "-" + LDSName = 'Version' + ESDName = "" + UDSName = "" + _domain = (0, 2**16-1) + _range = (0, 2**16-1) + _error = None + units = 'number' + + + +@SecurityLocalMetadataSet.add_parser +class ClassifyingCountryAndReleasingInstructionCCMVD(StringElementParser): + """ + """ + key = b'\x17' + TAG = 23 + UDSKey = "-" + LDSName = 'Classifying Country And Releasing Instruction Courntry Coding Method Version Date' + ESDName = "" + UDSName = "" + + +@SecurityLocalMetadataSet.add_parser +class ClassifyingCountryCodeMethodVersionDate(StringElementParser): + """ + """ + key = b'\x18' + TAG = 24 + UDSKey = "-" + LDSName = 'Classifying Country Code Method Version Date' + ESDName = "" + UDSName = "" diff --git a/klvdata/misb0601.py b/klvdata/misb0601.py index 3bd9793..2c2f180 100755 --- a/klvdata/misb0601.py +++ b/klvdata/misb0601.py @@ -35,7 +35,17 @@ class UnknownElement(UnknownElement): - pass + @property + def LDSName(self): + return "?" + + @property + def ESDName(self): + return "?" + + @property + def UDSName(self): + return "?" @StreamParser.add_parser @@ -44,7 +54,7 @@ class UASLocalMetadataSet(SetParser): """ key = hexstr_to_bytes('06 0E 2B 34 - 02 0B 01 01 – 0E 01 03 01 - 01 00 00 00') name = 'UAS Datalink Local Set' - key_length = 1 + key_length = None # A length of None means the key is a variable length BER OID parsers = {} _unknown_element = UnknownElement @@ -701,14 +711,14 @@ class GenericFlagData01(MappedElementParser): _error = None -# @UASLocalMetadataSet.add_parser -# class SecurityLocalMetadataSet(MappedElementParser): -# key = b'\x30' -# TAG = 48 -# UDSKey = "06 0E 2B 34 02 03 01 01 0E 01 03 03 02 00 00 00" -# LDSName = "Security Local Set" -# ESDName = "" -# UDSName = "Security Local Set" +@UASLocalMetadataSet.add_parser +class SecurityLocalMetadataSet(MappedElementParser): + key = b'\x30' + TAG = 48 + UDSKey = "06 0E 2B 34 02 03 01 01 0E 01 03 03 02 00 00 00" + LDSName = "Security Local Set" + ESDName = "" + UDSName = "Security Local Set" @UASLocalMetadataSet.add_parser @@ -1292,14 +1302,14 @@ class PlatformSideslipAngleFull(MappedElementParser): units = 'degrees' -#@UASLocalMetadataSet.add_parser -# class MIISCoreIdentifier(StringElementParser): -# key = b'\x5E' -# TAG = 94 -# UDSKey = "06 0E 2B 34 01 01 01 01 0E 01 04 05 03 00 00 00" -# LDSName = "MIIS Core Identifier" -# ESDName = "" -# UDSName = "Motion Imagery Identification System Core" +@UASLocalMetadataSet.add_parser +class MIISCoreIdentifier(BytesElementParser): + key = b'\x5E' + TAG = 94 + UDSKey = "06 0E 2B 34 01 01 01 01 0E 01 04 05 03 00 00 00" + LDSName = "MIIS Core Identifier" + ESDName = "" + UDSName = "Motion Imagery Identification System Core" #@UASLocalMetadataSet.add_parser diff --git a/klvdata/misbEG0104.py b/klvdata/misbEG0104.py index efdb57d..0446f78 100644 --- a/klvdata/misbEG0104.py +++ b/klvdata/misbEG0104.py @@ -36,7 +36,17 @@ class UnknownElement(UnknownElement): - pass + @property + def LDSName(self): + return "?" + + @property + def ESDName(self): + return "?" + + @property + def UDSName(self): + return "?" @StreamParser.add_parser @@ -266,7 +276,7 @@ class SensorRelativeAzimuthAngle(IEEE754ElementParser): _range = (0, 360) units = 'degrees' -@UAVBasicUniversalMetadataSet.add_parser +@UAVBasicUniversalMetadataSet.add_parser class SensorRelativeElevationAngle(IEEE754ElementParser): key = hexstr_to_bytes("06 0e 2b 34 01 01 01 01 07 01 10 01 03 00 00 00") TAG = 19 @@ -277,8 +287,8 @@ class SensorRelativeElevationAngle(IEEE754ElementParser): _domain = (-(2 ** 31 - 1), 2 ** 31 - 1) _range = (-180, 180) units = 'degrees' - -@UAVBasicUniversalMetadataSet.add_parser + +@UAVBasicUniversalMetadataSet.add_parser class SlantRange(IEEE754ElementParser): key = hexstr_to_bytes("06 0E 2B 34 01 01 01 01 07 01 08 01 01 00 00 00") TAG = 21 diff --git a/klvdata/setparser.py b/klvdata/setparser.py index b4701cd..7c9d320 100755 --- a/klvdata/setparser.py +++ b/klvdata/setparser.py @@ -104,18 +104,28 @@ def __str__(self): def MetadataList(self): ''' Return metadata dictionary''' - metadata = {} - def repeat(items, indent=1): + def repeat(items): + metadata = OrderedDict() for item in items: - try: - metadata[item.TAG] = (item.LDSName, item.ESDName, item.UDSName, str(item.value.value)) - except: - None + if hasattr(item, "TAG"): + tag = item.TAG + else: + tag = item.key + if hasattr(item, 'items'): - repeat(item.items.values(), indent + 1) - repeat(self.items.values()) - return OrderedDict(metadata) + value = repeat(item.items.values()) + elif hasattr(item.value, 'value'): + value = str(item.value.value) + else: + value = item.value + + metadata[tag] = (item.LDSName, item.ESDName, item.UDSName, value) + + return metadata + + return repeat(self.items.values()) + def structure(self): print(str(type(self)))