def bounded_composite_setter_preconditions(message: Message, field: Field) -> Sequence[Expr]: return [ Call( "Field_Condition", [ Variable("Ctx"), NamedAggregate(("Fld", Variable(field.affixed_name))) ] + ([Variable("Length")] if common.length_dependent_condition(message) else []), ), GreaterEqual( Call("Available_Space", [Variable("Ctx"), Variable(field.affixed_name)]), Variable("Length"), ), LessEqual( Add( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Variable("Length"), ), Div(Last(const.TYPES_BIT_INDEX), Number(2)), ), Or(*[ And( *[ Call("Valid", [Variable("Ctx"), Variable(field.affixed_name)]) for field in message.fields if Variable(field.name) in l.condition.variables() ], l.condition.substituted( mapping={ Variable(field.name): Call(f"Get_{field.name}", [Variable("Ctx")]) for field in message.fields if Variable(field.name) in l.condition.variables() }), ) for l in message.incoming(field) if Last("Message") in l.length ]), Equal( Mod( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Size(const.TYPES_BYTE), ), Number(1), ), Equal( Mod(Variable("Length"), Size(const.TYPES_BYTE)), Number(0), ), ]
def enumeration_functions(enum: Enumeration) -> List[Subprogram]: common_precondition = And( Less(Value('Offset'), Number(8)), Equal( Length('Buffer'), Add( Div(Add(Size(enum.base_name), Value('Offset'), Number(-1)), Number(8)), Number(1)))) control_expression = LogCall( f'Convert_To_{enum.base_name} (Buffer, Offset)') validation_expression: Expr if enum.always_valid: validation_expression = Value('True') else: validation_cases: List[Tuple[Expr, Expr]] = [] validation_cases.extend( (value, Value('True')) for value in enum.literals.values()) validation_cases.append((Value('others'), Value('False'))) validation_expression = CaseExpression(control_expression, validation_cases) validation_function = ExpressionFunction( f'Valid_{enum.name}', 'Boolean', [('Buffer', 'Types.Bytes'), ('Offset', 'Natural')], validation_expression, [Precondition(common_precondition)]) function_name = f'Convert_To_{enum.name}' parameters = [('Buffer', 'Types.Bytes'), ('Offset', 'Natural')] precondition = Precondition( And(common_precondition, LogCall(f'Valid_{enum.name} (Buffer, Offset)'))) conversion_cases: List[Tuple[Expr, Expr]] = [] conversion_function: Subprogram if enum.always_valid: conversion_cases.extend((value, Aggregate(Value('True'), Value(key))) for key, value in enum.literals.items()) conversion_cases.append( (Value('others'), Aggregate(Value('False'), Value('Raw')))) conversion_function = Function( function_name, enum.name, parameters, [Declaration('Raw', enum.base_name, control_expression)], [ReturnStatement(CaseExpression(Value('Raw'), conversion_cases))], [precondition]) else: conversion_cases.extend( (value, Value(key)) for key, value in enum.literals.items()) conversion_cases.append( (Value('others'), LogCall(f'Unreachable_{enum.name}'))) conversion_function = ExpressionFunction( function_name, enum.name, parameters, CaseExpression(control_expression, conversion_cases), [precondition]) return [validation_function, conversion_function]
def test_attribute() -> None: assert isinstance(Size("X"), Attribute) assert isinstance(Length("X"), Attribute) assert isinstance(First("X"), Attribute) assert isinstance(Last("X"), Attribute) assert isinstance(Range("X"), Attribute) assert isinstance(Old("X"), Attribute) assert isinstance(Result("X"), Attribute) assert isinstance(Constrained("X"), Attribute) assert First("X") == First(Variable("X")) assert First("X") == First(ID("X")) assert First("X") == First(Variable(ID("X")))
def unbounded_composite_setter_preconditions( message: Message, field: Field) -> Sequence[Expr]: return [ Call( "Field_Condition", [ Variable("Ctx"), NamedAggregate(("Fld", Variable(field.affixed_name))) ] + ([ Call( "Field_Length", [Variable("Ctx"), Variable(field.affixed_name)], ) ] if common.length_dependent_condition(message) else []), ), common.sufficient_space_for_field_condition( Variable(field.affixed_name)), Equal( Mod( Call("Field_First", [Variable("Ctx"), Variable(field.affixed_name)]), Size(const.TYPES_BYTE), ), Number(1), ), Equal( Mod( Call("Field_Length", [Variable("Ctx"), Variable(field.affixed_name)]), Size(const.TYPES_BYTE), ), Number(0), ), ]
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 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()
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()
"TLV_With_Checksum::Message", [ Link(INITIAL, Field("Tag")), Link(Field("Tag"), Field("Length"), Equal(Variable("Tag"), Variable("Msg_Data"))), Link(Field("Tag"), FINAL, Equal(Variable("Tag"), Variable("Msg_Error"))), Link(Field("Length"), Field("Value"), size=Mul(Variable("Length"), Number(8))), Link(Field("Value"), Field("Checksum")), Link(Field("Checksum"), FINAL, ValidChecksum("Checksum")), ], { Field("Tag"): TLV_WITH_CHECKSUM_TAG, Field("Length"): TLV_WITH_CHECKSUM_LENGTH, Field("Value"): OPAQUE, Field("Checksum"): TLV_WITH_CHECKSUM_CHECKSUM, }, checksums={ID("Checksum"): [Variable("Tag"), Size("Value"), Variable("Value")]}, skip_proof=True, ) TLV_WITH_CHECKSUM_MODEL = Model( [TLV_WITH_CHECKSUM_TAG, TLV_WITH_CHECKSUM_LENGTH, TLV_WITH_CHECKSUM_MESSAGE] ) NULL_MESSAGE_IN_TLV_MESSAGE = Refinement("In_TLV", TLV_MESSAGE, Field("Value"), NULL_MESSAGE) NULL_MESSAGE_IN_TLV_MESSAGE_MODEL = Model( [TLV_TAG, TLV_LENGTH, TLV_MESSAGE, NULL_MESSAGE, NULL_MESSAGE_IN_TLV_MESSAGE] ) ETHERNET_ADDRESS = ModularInteger("Ethernet::Address", Pow(Number(2), Number(48))) ETHERNET_TYPE_LENGTH = RangeInteger( "Ethernet::Type_Length", Number(46), Sub(Pow(Number(2), Number(16)), Number(1)), Number(16) )
def test_size_z3variables(self) -> None: self.assertEqual(Size("Z").variables(True), [Variable("Z'Size")])
def create_composite_setter_procedures(self, message: Message) -> UnitPart: def specification(field: Field) -> ProcedureSpecification: return ProcedureSpecification(f"Set_{field.name}", [InOutParameter(["Ctx"], "Context")]) def specification_bounded(field: Field) -> ProcedureSpecification: return ProcedureSpecification( f"Set_Bounded_{field.name}", [ InOutParameter(["Ctx"], "Context"), Parameter(["Length"], const.TYPES_BIT_LENGTH) ], ) def formal_parameters( field: Field) -> Sequence[FormalSubprogramDeclaration]: return [ FormalSubprogramDeclaration( ProcedureSpecification( f"Process_{field.name}", [OutParameter([field.name], const.TYPES_BYTES)], )), FormalSubprogramDeclaration( FunctionSpecification( "Valid_Length", "Boolean", [Parameter(["Length"], const.TYPES_LENGTH)], )), ] return UnitPart( [ SubprogramDeclaration( specification(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.unbounded_composite_setter_preconditions( message, f), Call( "Valid_Length", [ Call( const.TYPES_LENGTH, [ Div( Call( "Field_Length", [ Variable("Ctx"), Variable( f.affixed_name) ], ), Size(const.TYPES_BYTE), ), ], ), ], ), )), Postcondition( And( *self.composite_setter_postconditions( message, f), )), ], formal_parameters(f), ) for f, t in message.types.items() if isinstance(t, Opaque) and unbounded_setter_required(message, f) ] + [ SubprogramDeclaration( specification_bounded(f), [ Precondition( AndThen( *self.setter_preconditions(f), *self.bounded_composite_setter_preconditions( message, f), Call( "Valid_Length", [ Call( const.TYPES_LENGTH, [ Div(Variable("Length"), Size(const.TYPES_BYTE)) ], ) ], ), )), Postcondition( And( *self.composite_setter_postconditions( message, f), )), ], formal_parameters(f), ) for f, t in message.types.items() if isinstance(t, Opaque) and bounded_setter_required(message, f) ], [ SubprogramBody( specification(f), [ *common.field_bit_location_declarations( Variable(f.affixed_name)), 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")]), ), ], [ CallStatement(f"Initialize_{f.name}", [Variable("Ctx")]), CallStatement( f"Process_{f.name}", [ Slice( Selected(Variable("Ctx.Buffer"), "all"), Variable("Buffer_First"), Variable("Buffer_Last"), ), ], ), ], ) for f, t in message.types.items() if isinstance(t, Opaque) and unbounded_setter_required(message, f) ] + [ SubprogramBody( specification_bounded(f), [ ObjectDeclaration( ["First"], const.TYPES_BIT_INDEX, Call("Field_First", [Variable("Ctx"), Variable(f.affixed_name)]), True, ), ObjectDeclaration( ["Last"], const.TYPES_BIT_INDEX, Add(Variable("First"), Variable("Length"), -Number(1)), True, ), 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")]), ), ], [ CallStatement(f"Initialize_Bounded_{f.name}", [Variable("Ctx"), Variable("Length")]), CallStatement( f"Process_{f.name}", [ Slice( Selected(Variable("Ctx.Buffer"), "all"), Variable("Buffer_First"), Variable("Buffer_Last"), ), ], ), ], ) for f, t in message.types.items() if isinstance(t, Opaque) and bounded_setter_required(message, f) ], )
def test_size_z3variables() -> None: assert Size("Z").variables() == [Variable("Z")]