def test_sub_simplified(self) -> None: self.assertEqual(Sub(Number(1), Variable("X")).simplified(), Add(Variable("X"), Number(-1))) self.assertEqual(Sub(Variable("X"), Number(1)).simplified(), Add(Variable("X"), Number(-1))) self.assertEqual(Sub(Number(6), Number(2)).simplified(), Number(4)) self.assertEqual( Sub(Variable("X"), Variable("Y")).simplified(), Add(Variable("X"), Variable("Y", True)) )
def test_sub(self) -> None: self.assertEqual( Sub(Number(6), Number(4)).z3expr(), z3.IntVal(6) - z3.IntVal(4)) self.assertEqual( Sub(Number(12), Number(20)).z3expr(), z3.IntVal(12) - z3.IntVal(20))
def test_sub_simplified() -> None: assert Sub(Number(1), Variable("X")).simplified() == Add(Number(1), -Variable("X")) assert Sub(Variable("X"), Number(1)).simplified() == Add(Variable("X"), Number(-1)) assert Sub(Number(6), Number(2)).simplified() == Number(4) assert Sub(Variable("X"), Variable("Y")).simplified() == Add(Variable("X"), -Variable("Y")) assert ( Equal(Variable("Q"), Sub(Add(Variable("Y"), Variable("Q")), Variable("Y"))) .simplified() .simplified() == TRUE )
def test_sub_simplified(self) -> None: self.assertEqual( Sub(Number(1), Value('X')).simplified(), Add(Value('X'), Number(-1))) self.assertEqual( Sub(Value('X'), Number(1)).simplified(), Add(Value('X'), Number(-1))) self.assertEqual(Sub(Number(6), Number(2)).simplified(), Number(4)) self.assertEqual( Sub(Value('X'), Value('Y')).simplified(), Add(Value('X'), Value('Y', True)))
def test_distributivity_simplified(self) -> None: self.assertEqual( Add(Sub(Value('X'), Add(Value('X'), Number(1))), Add(Value('X'), Number(1))).simplified(), Value('X')) self.assertEqual( Div(Add(Mul(Value('X'), Number(8)), Number(144)), Number(8)).simplified(), Add(Value('X'), Number(18))) self.assertEqual( Div(Sub(Mul(Value('X'), Number(8)), Number(148)), Number(8)).simplified(), Add(Value('X'), Div(Number(-148), Number(8))))
def test_bin_expr_converted(self) -> None: self.assertEqual( Less(Variable("X"), Number(1)).converted( lambda x: Name(x.name) if isinstance(x, Variable) else x ), Less(Name("X"), Number(1)), ) self.assertEqual( Sub(Variable("X"), Number(1)).converted( lambda x: Name("Y") if x == Sub(Variable("X"), Number(1)) else x ), Name("Y"), )
def parse_mathematical_expression(string: str, location: int, tokens: ParseResults) -> Expr: result: List[Expr] = tokens[0] while len(result) > 1: left = result.pop(0) operator = result.pop(0) right = result.pop(0) assert left.location, f'expression "{left}" without location' assert right.location, f'expression "{right}" without location' assert left.location.source == right.location.source, "expression with different source" locn = Location(left.location.start, left.location.source, left.location.end) expression: Expr if operator == "+": expression = Add(left, right) expression.location = locn elif operator == "-": expression = Sub(left, right, locn) elif operator == "*": expression = Mul(left, right) expression.location = locn elif operator == "/": expression = Div(left, right, locn) elif operator == "**": expression = Pow(left, right, locn) else: raise ParseFatalException(string, location, "unexpected mathematical operator") result.insert(0, expression) return result[0]
def test_range_size(self) -> None: self.assertEqual(RangeInteger('UINT32', Number(0), Sub(Pow(Number(2), Number(32)), Number(1)), Number(32) ).size, Number(32))
def test_term_simplified(self) -> None: self.assertEqual( Add( Mul(Number(1), Number(6)), Sub(Variable("X"), Number(10)), Add(Number(1), Number(3)) ).simplified(), Variable("X"), )
def test_expr_variables_duplicates(self) -> None: self.assertEqual( And(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual( Or(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual( Add(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual( Mul(Variable("X"), Variable("Y"), Variable("X")).variables(), [Variable("X"), Variable("Y")], ) self.assertEqual(Sub(Variable("X"), Variable("X")).variables(), [Variable("X")]) self.assertEqual(Div(Variable("X"), Variable("X")).variables(), [Variable("X")]) self.assertEqual( Or( Greater(Variable("X"), Number(42)), And(TRUE, Less(Variable("X"), Number(1))) ).variables(), [Variable("X")], )
def test_range_size() -> None: assert_equal( RangeInteger("P.T", Number(0), Sub(Pow(Number(2), Number(32)), Number(1)), Number(32)).size, Number(32), )
def field_length(field: Field) -> Expr: if public: return Call("Field_Length", [Variable("Ctx"), Variable(field.affixed_name)]) return Add( Sub( Selected(Indexed(cursors, Variable(field.affixed_name)), "Last"), Selected(Indexed(cursors, Variable(field.affixed_name)), "First"), ), Number(1), )
def test_valid_use_message_first_last() -> None: structure = [ Link( INITIAL, Field("Verify_Data"), length=Add(Sub(Last("Message"), First("Message")), Number(1)), ), Link(Field("Verify_Data"), FINAL), ] types = {Field("Verify_Data"): Opaque()} Message("P.M", structure, types)
def test_bin_expr_substituted() -> None: assert_equal( Less(Variable("X"), Number(1)).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else x ), Less(Variable("P_X"), Number(1)), ) assert_equal( Sub(Variable("X"), Number(1)).substituted( lambda x: Variable("Y") if x == Sub(Variable("X"), Number(1)) else x ), Variable("Y"), ) assert_equal( NotEqual(Variable("X"), Number(1)).substituted( lambda x: Variable(f"P_{x}") if isinstance(x, Variable) else (Equal(x.left, x.right) if isinstance(x, NotEqual) else x) ), Equal(Variable("P_X"), Number(1)), )
def test_mathematical_expression_precedence() -> None: assert_equal( grammar.mathematical_expression().parseString("A - B * 2**3 - 1")[0], Sub(Sub(Variable("A"), Mul(Variable("B"), Pow(Number(2), Number(3)))), Number(1)), ) assert_equal( grammar.mathematical_expression().parseString("(A - B) * 2**3 - 1")[0], Sub(Mul(Sub(Variable("A"), Variable("B")), Pow(Number(2), Number(3))), Number(1)), ) assert_equal( grammar.mathematical_expression().parseString("A - B * 2**(3 - 1)")[0], Sub(Variable("A"), Mul(Variable("B"), Pow(Number(2), Sub(Number(3), Number(1))))), ) assert_equal( grammar.mathematical_expression().parseString("A - (B * 2)**3 - 1")[0], Sub(Sub(Variable("A"), Pow(Mul(Variable("B"), Number(2)), Number(3))), Number(1)), ) assert_equal( grammar.mathematical_expression().parseString("A - (B * 2**3 - 1)")[0], Sub(Variable("A"), Sub(Mul(Variable("B"), Pow(Number(2), Number(3))), Number(1))), )
def substitution(self, message: Message, prefix: bool = True) -> Mapping[Name, Expr]: def prefixed(name: str) -> Expr: return Selected(Name("Ctx"), name) if prefix else Name(name) first = prefixed("First") last = prefixed("Last") cursors = prefixed("Cursors") return { **{First("Message"): first}, **{Last("Message"): last}, **{ First(f.name): Selected(Indexed(cursors, Name(f.affixed_name)), "First") for f in message.fields }, **{ Last(f.name): Selected(Indexed(cursors, Name(f.affixed_name)), "Last") for f in message.fields }, **{ Length(f.name): Add( Sub( Selected(Indexed(cursors, Name(f.affixed_name)), "Last"), Selected(Indexed(cursors, Name(f.affixed_name)), "First"), ), Number(1), ) for f in message.fields }, **{ Variable(f.name): Call( self.types.bit_length, [Selected(Indexed(cursors, Name(f.affixed_name)), f"Value.{f.name}_Value")], ) for f, t in message.types.items() if not isinstance(t, Enumeration) }, **{ Variable(f.name): Call( self.types.bit_length, [Selected(Indexed(cursors, Name(f.affixed_name)), f"Value.{f.name}_Value")], ) for f, t in message.types.items() if isinstance(t, Enumeration) }, **{ Variable(l): Call(self.types.bit_length, [Call("Convert", [Name(l)])]) for l in itertools.chain.from_iterable( t.literals.keys() for t in message.types.values() if isinstance(t, Enumeration) ) }, }
def __link_expression(self, link: Link) -> Expr: name = link.target.name return And( *[ Equal(First(name), self.__target_first(link)), Equal(Length(name), self.__target_length(link)), Equal(Last(name), self.__target_last(link)), GreaterEqual(First("Message"), Number(0)), GreaterEqual(Last("Message"), Last(name)), GreaterEqual(Last("Message"), First("Message")), Equal(Length("Message"), Add(Sub(Last("Message"), First("Message")), Number(1))), link.condition, ] )
def test_invalid_negative_field_length_modular() -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), length=Sub(Variable("F1"), Number(2))), Link(Field("F2"), FINAL), ] types = { Field("F1"): MODULAR_INTEGER, Field("F2"): Opaque(), } assert_message_model_error( structure, types, r'^model: error: negative length for field "F2" [(]F1 -> F2[)]$', )
def create_ethernet_pdu() -> PDU: uint48 = ModularInteger('UINT48', Pow(Number(2), Number(48))) uint16 = RangeInteger('UINT16', Number(0), Sub(Pow(Number(2), Number(16)), Number(1)), Number(16)) payload_array = Array('Payload_Array') initial = InitialNode() destination = Node('Destination', uint48) source = Node('Source', uint48) tpid = Node('TPID', uint16) tci = Node('TCI', uint16) ether_type = Node('EtherType', uint16) payload = Node('Payload', payload_array) initial.edges = [Edge(destination)] destination.edges = [Edge(source)] source.edges = [Edge(tpid)] tpid.edges = [Edge(tci, Equal(Value('TPID'), Number(0x8100))), Edge(ether_type, NotEqual(Value('TPID'), Number(0x8100)), first=First('TPID'))] tci.edges = [Edge(ether_type)] ether_type.edges = [Edge(payload, LessEqual(Value('EtherType'), Number(1500)), Mul(LengthValue('EtherType'), Number(8))), Edge(payload, GreaterEqual(Value('EtherType'), Number(1536)), Sub(Last('Message'), Last('EtherType')))] payload.edges = [Edge(FINAL, And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))))] return PDU('Ethernet.Frame', initial)
def test_no_verification_icmp_checksum( icmp_checksum_message_value: MessageValue, icmp_message: Message) -> None: test_data = ( b"\x47\xb4\x67\x5e\x00\x00\x00\x00" b"\x4a\xfc\x0d\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17" b"\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" b"\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37") icmp_checksum_unv = MessageValue( icmp_message.copy( structure=[ Link(l.source, l.target, condition=And(l.condition, ValidChecksum("Checksum"))) if l.target == FINAL else l for l in icmp_message.structure ], checksums={ ID("Checksum"): [ ValueRange(First("Tag"), Sub(First("Checksum"), Number(1))), Size("Checksum"), ValueRange(Add(Last("Checksum"), Number(1)), Last("Message")), ] }, ), skip_verification=True, ) icmp_checksum_message_value.set_checksum_function( {"Checksum": icmp_checksum_function}) icmp_checksum_message_value.set("Tag", "Echo_Request") icmp_checksum_message_value.set("Code_Zero", 0) icmp_checksum_message_value.set("Identifier", 5) icmp_checksum_message_value.set("Sequence_Number", 1) icmp_checksum_message_value.set("Data", test_data) icmp_checksum_unv.set_checksum_function( {"Checksum": icmp_checksum_function}) icmp_checksum_unv.set("Tag", "Echo_Request") icmp_checksum_unv.set("Code_Zero", 0) icmp_checksum_unv.set("Checksum", 0) icmp_checksum_unv.set("Identifier", 5) icmp_checksum_unv.set("Sequence_Number", 1) icmp_checksum_unv.set("Data", test_data) icmp_checksum_unv.update_checksums() assert icmp_checksum_unv.valid_message assert icmp_checksum_unv.get( "Checksum") == icmp_checksum_message_value.get("Checksum") assert icmp_checksum_unv.bytestring == icmp_checksum_message_value.bytestring
def test_invalid_negative_field_length_range_integer() -> None: o = Field(ID("O", location=Location((44, 3)))) structure = [ Link(INITIAL, Field("L")), Link( Field("L"), o, length=Mul(Number(8), Sub(Variable("L"), Number(50))), ), Link(o, FINAL), ] types = {Field("L"): RANGE_INTEGER, o: Opaque()} assert_message_model_error( structure, types, r'^<stdin>:44:3: model: error: negative length for field "O" [(]L -> O[)]$', )
def test_field_after_message_start(self) -> None: foo_type = ModularInteger("P.Foo", Pow(Number(2), Number(32))) structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), first=Sub(First("Message"), Number(1000))), Link(Field("F2"), FINAL), ] types = {Field("F1"): foo_type, Field("F2"): foo_type} with mock.patch("rflx.model.Message._Message__verify_conditions", lambda x: None): with self.assertRaisesRegex( ModelError, '^start of field "F2" on path F1 -> F2 before' " message start"): Message("P.M", structure, types)
def field_byte_location_declarations(self) -> Sequence[Declaration]: return [ ExpressionFunctionDeclaration( FunctionSpecification("Buffer_First", self.types.index), Call(self.types.byte_index, [Name("First")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_Last", self.types.index), Call(self.types.byte_index, [Name("Last")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Offset", self.types.offset), Call( self.types.offset, [Mod(Sub(Number(8), Mod(Name("Last"), Number(8))), Number(8))], ), ), ]
def field_byte_location_declarations() -> Sequence[Declaration]: return [ ExpressionFunctionDeclaration( FunctionSpecification("Buffer_First", const.TYPES_INDEX), Call(const.TYPES_BYTE_INDEX, [Variable("First")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Buffer_Last", const.TYPES_INDEX), Call(const.TYPES_BYTE_INDEX, [Variable("Last")]), ), ExpressionFunctionDeclaration( FunctionSpecification("Offset", const.TYPES_OFFSET), Call( const.TYPES_OFFSET, [Mod(Sub(Number(8), Mod(Variable("Last"), Number(8))), Number(8))], ), ), ]
def test_field_after_message_start(monkeypatch: Any) -> None: structure = [ Link(INITIAL, Field("F1")), Link(Field("F1"), Field("F2"), first=Sub(First("Message"), Number(1000))), Link(Field("F2"), FINAL), ] types = {Field("F1"): MODULAR_INTEGER, Field("F2"): MODULAR_INTEGER} monkeypatch.setattr(Message, "_AbstractMessage__verify_conditions", lambda x: None) assert_message_model_error( structure, types, r"^" r'model: error: negative start for field "F2" [(]F1 -> F2[)]\n' r'model: info: unsatisfied "Message\'First - 1000 >= Message\'First"' r"$", )
def parse_mathematical_expression(string: str, location: int, tokens: list) -> MathExpr: result: List[MathExpr] = tokens[0] while len(result) > 1: left = result.pop(0) operator = result.pop(0) right = result.pop(0) expression: MathExpr if operator == '+': expression = Add(left, right) elif operator == '-': expression = Sub(left, right) elif operator == '*': expression = Mul(left, right) elif operator == '/': expression = Div(left, right) elif operator == '**': expression = Pow(left, right) else: raise ParseFatalException(string, location, 'unexpected mathematical operator') result.insert(0, expression) return result[0]
def parse_mathematical_expression(string: str, location: int, tokens: ParseResults) -> Expr: result: List[Expr] = tokens[0] while len(result) > 1: left = result.pop(0) operator = result.pop(0) right = result.pop(0) expression: Expr if operator == "+": expression = Add(left, right) elif operator == "-": expression = Sub(left, right) elif operator == "*": expression = Mul(left, right) elif operator == "/": expression = Div(left, right) elif operator == "**": expression = Pow(left, right) else: raise ParseFatalException(string, location, "unexpected mathematical operator") result.insert(0, expression) return result[0]
def message_structure_invariant( self, message: Message, link: Link = None, prefix: bool = True ) -> Expr: def prefixed(name: str) -> Expr: return Selected(Name("Ctx"), name) if prefix else Name(name) if not link: return self.message_structure_invariant(message, message.outgoing(INITIAL)[0], prefix) source = link.source target = link.target if target is FINAL: return TRUE field_type = message.types[target] condition = link.condition.simplified(self.substitution(message, prefix)) length = ( Size(base_type_name(field_type)) if isinstance(field_type, Scalar) else link.length.simplified(self.substitution(message, prefix)) ) first = ( Name(prefixed("First")) if source == INITIAL else link.first.simplified( { **self.substitution(message, prefix), **{ UNDEFINED: Add( Selected( Indexed(prefixed("Cursors"), Name(source.affixed_name)), "Last" ), Number(1), ) }, } ) ) return If( [ ( AndThen( Call( "Structural_Valid", [Indexed(prefixed("Cursors"), Name(target.affixed_name))], ), condition, ), AndThen( Equal( Add( Sub( Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "Last", ), Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "First", ), ), Number(1), ), length, ), Equal( Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "Predecessor", ), Name(source.affixed_name), ), Equal( Selected( Indexed(prefixed("Cursors"), Name(target.affixed_name)), "First" ), first, ), *[ self.message_structure_invariant(message, l, prefix) for l in message.outgoing(target) ], ), ) ] ).simplified()
def test_pdu_fields_ethernet(self) -> None: expected = OrderedDict([ ('Destination', Field('Destination', ModularInteger('UINT48', Pow(Number(2), Number(48))), TRUE, { '0': Variant( [], TRUE, { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47) }) })), ('Source', Field('Source', ModularInteger('UINT48', Pow(Number(2), Number(48))), TRUE, { '00': Variant( [ ('Destination', '0') ], TRUE, { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95) }) })), ('TPID', Field('TPID', RangeInteger('UINT16', Number(0), Sub(Pow(Number(2), Number(16)), Number(1)), Number(16)), Or(NotEqual(Value('TPID'), Number(0x8100)), Equal(Value('TPID'), Number(0x8100))), { '000': Variant( [ ('Destination', '0'), ('Source', '00') ], TRUE, { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111) }) })), ('TCI', Field('TCI', RangeInteger('UINT16', Number(0), Sub(Pow(Number(2), Number(16)), Number(1)), Number(16)), TRUE, { '0000': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000') ], Equal(Value('TPID'), Number(0x8100)), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('TCI'): Number(16), First('TCI'): Number(112), Last('TCI'): Number(127) }) })), ('EtherType', Field('EtherType', RangeInteger('UINT16', Number(0), Sub(Pow(Number(2), Number(16)), Number(1)), Number(16)), Or(GreaterEqual(Value('EtherType'), Number(1536)), LessEqual(Value('EtherType'), Number(1500))), { '00000': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('TCI', '0000') ], TRUE, { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('TCI'): Number(16), First('TCI'): Number(112), Last('TCI'): Number(127), Length('EtherType'): Number(16), First('EtherType'): Number(128), Last('EtherType'): Number(143) }), '0001': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000') ], NotEqual(Value('TPID'), Number(0x8100)), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('EtherType'): Number(16), First('EtherType'): Number(96), Last('EtherType'): Number(111) }) })), ('Payload', Field('Payload', Array('Payload_Array'), And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))), { '000000': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('TCI', '0000'), ('EtherType', '00000') ], LessEqual(Value('EtherType'), Number(1500)), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('TCI'): Number(16), First('TCI'): Number(112), Last('TCI'): Number(127), Length('EtherType'): Number(16), First('EtherType'): Number(128), Last('EtherType'): Number(143), Length('Payload'): Mul(LengthValue('EtherType'), Number(8)), First('Payload'): Number(144), Last('Payload'): Add(Mul(LengthValue('EtherType'), Number(8)), Number(143)) }), '000001': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('TCI', '0000'), ('EtherType', '00000') ], GreaterEqual(Value('EtherType'), Number(1536)), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('TCI'): Number(16), First('TCI'): Number(112), Last('TCI'): Number(127), Length('EtherType'): Number(16), First('EtherType'): Number(128), Last('EtherType'): Number(143), Length('Payload'): Add(Last('Message'), Number(-143)), First('Payload'): Number(144), Last('Payload'): Last('Message') }), '00010': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('EtherType', '0001') ], LessEqual(Value('EtherType'), Number(1500)), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('EtherType'): Number(16), First('EtherType'): Number(96), Last('EtherType'): Number(111), Length('Payload'): Mul(LengthValue('EtherType'), Number(8)), First('Payload'): Number(112), Last('Payload'): Add(Mul(LengthValue('EtherType'), Number(8)), Number(111)) }), '00011': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('EtherType', '0001') ], GreaterEqual(Value('EtherType'), Number(1536)), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('EtherType'): Number(16), First('EtherType'): Number(96), Last('EtherType'): Number(111), Length('Payload'): Add(Last('Message'), Number(-111)), First('Payload'): Number(112), Last('Payload'): Last('Message') }) })), ('FINAL', Field('FINAL', Null(), TRUE, { '0000000': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('TCI', '0000'), ('EtherType', '00000'), ('Payload', '000000') ], And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('TCI'): Number(16), First('TCI'): Number(112), Last('TCI'): Number(127), Length('EtherType'): Number(16), First('EtherType'): Number(128), Last('EtherType'): Number(143), Length('Payload'): Mul(LengthValue('EtherType'), Number(8)), First('Payload'): Number(144), Last('Payload'): Add(Mul(LengthValue('EtherType'), Number(8)), Number(143)) }), '0000010': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('TCI', '0000'), ('EtherType', '00000'), ('Payload', '000001') ], And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('TCI'): Number(16), First('TCI'): Number(112), Last('TCI'): Number(127), Length('EtherType'): Number(16), First('EtherType'): Number(128), Last('EtherType'): Number(143), Length('Payload'): Add(Last('Message'), Number(-143)), First('Payload'): Number(144), Last('Payload'): Last('Message') }), '000100': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('EtherType', '0001'), ('Payload', '00010') ], And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('EtherType'): Number(16), First('EtherType'): Number(96), Last('EtherType'): Number(111), Length('Payload'): Mul(LengthValue('EtherType'), Number(8)), First('Payload'): Number(112), Last('Payload'): Add(Mul(LengthValue('EtherType'), Number(8)), Number(111)) }), '000110': Variant( [ ('Destination', '0'), ('Source', '00'), ('TPID', '000'), ('EtherType', '0001'), ('Payload', '00011') ], And(GreaterEqual(Div(Length('Payload'), Number(8)), Number(46)), LessEqual(Div(Length('Payload'), Number(8)), Number(1500))), { Length('Destination'): Number(48), First('Destination'): Number(0), Last('Destination'): Number(47), Length('Source'): Number(48), First('Source'): Number(48), Last('Source'): Number(95), Length('TPID'): Number(16), First('TPID'): Number(96), Last('TPID'): Number(111), Length('EtherType'): Number(16), First('EtherType'): Number(96), Last('EtherType'): Number(111), Length('Payload'): Add(Last('Message'), Number(-111)), First('Payload'): Number(112), Last('Payload'): Last('Message') }) })) ]) self.assertEqual(ETHERNET_PDU.fields(), expected)
def message_structure_invariant( message: Message, prefix: str, link: Link = None, embedded: bool = False ) -> Expr: def prefixed(name: str) -> Expr: return Selected(Variable("Ctx"), name) if not embedded else Variable(name) if not link: return message_structure_invariant(message, prefix, message.outgoing(INITIAL)[0], embedded) source = link.source target = link.target if target is FINAL: return TRUE field_type = message.types[target] condition = link.condition.substituted(substitution(message, embedded)).simplified() length = ( Size(prefix * full_base_type_name(field_type)) if isinstance(field_type, Scalar) else link.length.substituted( substitution(message, embedded, target_type=const.TYPES_BIT_LENGTH) ).simplified() ) first = ( prefixed("First") if source == INITIAL else link.first.substituted(substitution(message, embedded)) .substituted( mapping={ UNDEFINED: Add( Selected(Indexed(prefixed("Cursors"), Variable(source.affixed_name)), "Last"), Number(1), ) } ) .simplified() ) return If( [ ( AndThen( Call( "Structural_Valid", [Indexed(prefixed("Cursors"), Variable(target.affixed_name))], ), condition, ), AndThen( Equal( Add( Sub( Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "Last", ), Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "First", ), ), Number(1), ), length, ), Equal( Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "Predecessor", ), Variable(source.affixed_name), ), Equal( Selected( Indexed(prefixed("Cursors"), Variable(target.affixed_name)), "First" ), first, ), *[ message_structure_invariant(message, prefix, l, embedded) for l in message.outgoing(target) ], ), ) ] ).simplified()