def f(client: LedgerClient) -> DeviceResponse: response = client.apdu_exchange(INS, data, P1, P2) response_template = Struct( public_key=Prefixed(Int8ub, GreedyBytes), address=PascalString(Int8ub, "ascii"), chain_code=Bytes(32), ) parsed_response = response_template.parse(response) return DeviceResponse( public_key=parsed_response.public_key, address=parsed_response.address, chain_code=parsed_response.chain_code, )
def get_frame_format(cls) -> Struct: """ Returns Struct object containing frame structure. """ frame_format = Struct( frame_signature=Bytes(FRAME_SIGNATURE_BYTES_LENGTH), signed_part_of_the_frame=Struct( request_id=BytesInteger(FRAME_REQUEST_ID_BYTES_LENGTH), payload_type=Enum(Byte, PayloadType), payload=Prefixed(VarInt, GreedyBytes), ), ) assert FRAME_PAYLOAD_STARTING_BYTE == ( frame_format.frame_signature.subcon.length + frame_format.signed_part_of_the_frame.request_id.subcon.length + FRAME_PAYLOAD_TYPE_LENGTH ) return frame_format
def f(client: LedgerClient) -> DeviceResponse: response = client.apdu_exchange(INS, data, P1, P2) struct_kwargs = dict( public_key=Prefixed(Int8ub, GreedyBytes), address=PascalString(Int8ub, "ascii"), ) if opts.return_chain_code: struct_kwargs["chain_code"] = Bytes(32) response_template = Struct(**struct_kwargs) parsed_response = response_template.parse(response) return DeviceResponse( public_key=parsed_response.public_key, address=parsed_response.address, chain_code=parsed_response.chain_code if opts.return_chain_code else None, )
def test_complex_prefixed(): s = Struct( 'v' / Byte, 'a1' / Prefixed(Byte, Array(2, Struct('value' / DeferredValue(Byte)))), 'a2' / Array( 2, Struct( '@offset' / Tell, WriteDeferredValue(this['@offset'], lambda this: this._.a1[this._index].value), 'value' / Byte))) # parsing assert s.parse(b'\xff\x02\x41\x42\xe0\xe1') == { 'v': 0xff, 'a1': [{ 'value': 0x41 }, { 'value': 0x42 }], 'a2': [{ '@offset': 4, 'value': 0xe0 }, { '@offset': 5, 'value': 0xe1 }] } # building assert s.build({ 'v': 0xff, 'a1': [{}, {}], 'a2': [{ 'value': 0xe0 }, { 'value': 0xe1 }] }) == b'\xff\x02\x04\x05\xe0\xe1'
'comment': 1, 'cipher_id': 2, 'compression_flags': 3, 'master_seed': 4, 'transform_seed': 5, 'transform_rounds': 6, 'encryption_iv': 7, 'protected_stream_key': 8, 'stream_start_bytes': 9, 'protected_stream_id': 10, }), "data" / Prefixed( Int16ul, Switch(this.id, { 'compression_flags': CompressionFlags, 'cipher_id': CipherId, 'transform_rounds': Int32ul, 'protected_stream_id': ProtectedStreamId }, default=GreedyBytes)), ) DynamicHeader = DynamicDict( 'id', RepeatUntil(lambda item, a, b: item.id == 'end', DynamicHeaderItem)) # -------------------- Payload Verification -------------------- # encrypted payload is split into multiple data blocks with hashes PayloadBlock = Struct( "block_index" / Checksum(Int32ul, lambda this: this._index, this), "block_hash_offset" / Tell,
) dtx_message_payload_header_struct = Struct( 'flags' / Int32ul, 'auxiliaryLength' / Int32ul, 'totalLength' / Int64ul, ) message_aux_t_struct = Struct( 'magic' / Default(Int64ul, 0x1f0), 'aux' / Prefixed( Int64ul, GreedyRange( Struct( '_empty_dictionary' / Select(Const(0xa, Int32ul), Int32ul), 'type' / Int32ul, 'value' / Switch(this.type, { 2: BplitAdapter(Prefixed(Int32ul, GreedyBytes)), 3: Int32ul, 4: Int64ul }, default=GreedyBytes), )))) class MessageAux: def __init__(self): self.values = [] def append_int(self, value: int): self.values.append({'type': 3, 'value': value}) return self
# mapping script_object_types[0x05] = PrefixedArray(FactorioInt32ul, ScriptKeyValuePair) # some kind of reference script_object_types[0x06] = Int16ul ScriptDat = Struct( "_type" / Computed(lambda this: "script"), "version" / Version, "data" / PrefixedArray( Int32ul, Struct( "name" / PascalString(Int8ul, "latin-1"), "dump" / Prefixed( FactorioInt32ul, Struct( "version" / Version, "data" / ScriptSerializedObject, )), "tabletop_" / Int8ul)), Terminated, ) ModSettingsDat = Struct( "_type" / Computed(lambda this: "mod-settings"), "version" / Version, "settings" / SerializedObject, Terminated, ) IdEntry = Struct( "type" / PascalString(Int8ul, "latin-1"), "names" / PrefixedArray(
PascalString, BytesInteger, GreedyBytes, Struct, FocusedSeq, Prefixed, Select, Rebuild, Terminated, this ) from .compat import Const # RFC 4251 section 5 SshBytes = Prefixed(Int32ub, GreedyBytes) SshString = PascalString(Int32ub, 'utf8') SshMPInt = Select( Const(0, Int32ub), # zero stored as zero bytes of data FocusedSeq( 'num', 'len' / Rebuild(Int32ub, lambda ctx: int(ctx.num.bit_length() // 8 + 1)), 'num' / BytesInteger(this.len, signed=True), ), ) # RFC 4253 section 6.6 SshRSAKeyBlob = Struct( 'algo' / Const('ssh-rsa', SshString), 'e' / SshMPInt,
elements = elements[1:] for element in elements: if element.endswith("'"): out.append(0x80000000 | int(element[:-1])) else: out.append(int(element)) return out Bip32Path = Bip32PathAdapter(PrefixedArray(Byte, Int32ub)) PrefixedString = PascalString(Asn1Length, "utf8") AppName = PrefixedString Version = PrefixedString Icon = Prefixed(Asn1Length, GreedyBytes) CURVE_SEPCK256K1 = 1 CURVE_PRIME256R1 = 2 CURVE_ED25519 = 4 Curve = FlagsEnum( Byte, secp256k1=CURVE_SEPCK256K1, prime256r1=CURVE_PRIME256R1, ed25519=CURVE_ED25519 ) DerivationPath = Prefixed( Asn1Length, Struct(curve=Curve, paths=Optional(GreedyRange(Bip32Path))) ) Dependency = Prefixed( Asn1Length, Struct(name=PrefixedString, version=Optional(PrefixedString))
extras_header = "extras_header" / Struct( "offset" / Tell, "count" / Int32ub, "sections" / Array(this.count, Select(productinfo_entry, extra_entry)), "offset_end" / Tell, ) index_entry = "index_entry" / Struct( "offset" / Int32ub, "size" / Int32ub, "flags" / Int32ub, "name" / CString(encoding='ascii'), ) index_header = "index_header" / Struct( "entries" / Prefixed(Int32ub, GreedyRange(index_entry)), ) # Helper method to determine if a MAR file has signatures or not def _has_sigs(ctx): """Determine if a MAR file has a signature section or not. It does this by looking at where file data starts in the file. If this starts immediately after the headers (at offset 8), then it's an old style MAR that has no signatures. Args: ctx (context): construct parsing context Returns:
"functions" / Int32ub, Bytes(lambda ctx: ctx.headerlen - 16), Bytes(lambda ctx: ctx._.size - ctx.headerlen - 4), ) ExpT = Struct("entry" / PrefixedArray( Int32ub, Struct("function" / Int32ub, "arity" / Int32ub, "label" / Int32ub))) ImpT = Struct("entry" / PrefixedArray( Int32ub, Struct("module" / Int32ub, "function" / Int32ub, "arity" / Int32ub))) uncomp_chunk_litt = Struct( "entry" / PrefixedArray(Int32ub, Prefixed(Int32ub, Struct("term" / external_term)))) LitT = Struct( Int32ub, "data" / Prefixed(Computed(lambda ctx: ctx._.size - 4), Compressed(uncomp_chunk_litt, "zlib"))) LocT = PrefixedArray( Int32ub, Struct("function" / Int32ub, "arity" / Int32ub, "label" / Int32ub)) chunk = Struct( "chunk_name" / Bytes(4), "size" / Int32ub, "payload" / Aligned( 4, FixedSized( this.size,
#!/usr/bin/env python3 from construct import Struct, Int8ul, PaddedString, Const, PascalString, Int32ul, Compressed, GreedyBytes, Prefixed, PrefixedArray, Bytes, Int64ul, Padding, Int16ul, Int16ub, Int32ub, Tell, Pointer IrdMagic = Const("3IRD", PaddedString(4, encoding="ascii")) Md5Sum = Bytes(16) IrdFile = Struct("sector" / Int64ul, "hash" / Md5Sum) IrdBase = Struct("magic" / IrdMagic, "version" / Int8ul, "game_id" / PaddedString(9, "ascii"), "game_name" / PascalString(Int8ul, "utf-8"), "update_version" / PaddedString(4, "ascii"), "game_version" / PaddedString(5, "ascii"), "app_version" / PaddedString(5, "ascii"), "header" / Prefixed(Int32ul, Compressed(GreedyBytes, "gzip")), "footer" / Prefixed(Int32ul, Compressed(GreedyBytes, "gzip")), "regions" / PrefixedArray(Int8ul, Md5Sum), "files" / PrefixedArray(Int32ul, IrdFile))
"stamp" / Int32ub, "machine_name" / Default(PascalString(Int32ub, encoding="ascii"), ""), "uid" / Default(Int32ub, 0), "gid" / Default(Int32ub, 0), "gids" / Default(Int32ub, 0) # should be length-prefixed array? ) RpcAuthShort = Pass RpcAuthDes = Pass RpcOpaqueAuth = Struct( "flavor" / Default(RpcAuthFlavor, "null"), "content" / Prefixed( Int32ub, Switch( this.flavor, { "null": Pass, "unix": RpcAuthUnix, "short": RpcAuthShort, "des": RpcAuthDes }))) PortmapPort = 111 PortmapVersion = 2 PortmapProcedure = Enum(Int32ub, null=0, set=1, unset=2, getport=3, dump=4, call_result=5)
"id" / Int32ub, "creation" / Int8ub), encoder=lambda obj, ctx: (obj.node, obj.id, obj.creation), decoder=lambda obj, ctx: Port(*obj)) pid = ExprAdapter(Sequence("node" / term_, "id" / Int32ub, "serial" / Int32ub, "creation" / Int8ub), encoder=lambda obj, ctx: (obj.node, obj.id, obj.serial, obj.creation), decoder=lambda obj, ctx: Pid(*obj)) small_tuple = TupleAdapter(PrefixedArray(Int8ub, term_)) large_tuple = TupleAdapter(PrefixedArray(Int32ub, term_)) nil = ExprAdapter(Sequence(), encoder=lambda obj, ctx: (), decoder=lambda obj, ctx: []) string = ExprAdapter(Prefixed(Int16ub, GreedyBytes), encoder=lambda obj, ctx: obj.value, decoder=lambda obj, ctx: etString(obj)) list_ = ListAdapter(Sequence("len" / Int32ub, Array(this.len, term_), term_)) binary = ExprAdapter(Prefixed(Int32ub, GreedyBytes), encoder=lambda obj, ctx: obj.value, decoder=lambda obj, ctx: Binary(obj)) small_big = BigInteger(Int8ub) large_big = BigInteger(Int32ub) new_reference = ExprAdapter(Sequence("len" / Int16ub, "node" / term_, "creation" / Int8ub, "id" / Array(this.len, Int32ub)), encoder=lambda obj, ctx: (len(obj.id), obj.node, obj.creation, obj.id),
return obj - 8 def _encode(self, obj, context, path): return obj + 8 struct_blit_pixel = Struct('r' / Int8ul, 'g' / Int8ul, 'b' / Int8ul, 'a' / Int8ul) struct_blit_image_compressed = Struct( 'header' / Const(b'SPRITE'), 'type' / PaddedString(2, 'ASCII'), 'data' / Prefixed(ImageSizeAdapter(Int32ul), Struct( 'width' / Int16ul, 'height' / Int16ul, 'format' / Const(0x02, Int8ul), 'palette' / PrefixedArray(PaletteCountAdapter(Int8ul), struct_blit_pixel), 'pixels' / GreedyBytes, ), includelength=True)) struct_blit_image = ImageCompressor(struct_blit_image_compressed) struct_blit_meta = Struct( 'header' / Const(b'BLITMETA'), 'data' / Prefixed( Int16ul, Struct( 'checksum' / Checksum(Int32ul, lambda data: binascii.crc32(data), this._._.bin.data), 'date' / PaddedString(16, 'ascii'), 'title' / PaddedString(25, 'ascii'), 'description' / PaddedString(129, 'ascii'), 'version' /
'bit_length' / Computed(compute_bit_length), 'data_length' / Computed(compute_data_length), 'data' / Array(this.data_length, Int8ul) ) struct_blit_meta = Struct( 'header' / Const(b'BLITMETA'), 'data' / Prefixed(Int16ul, Struct( 'checksum' / Checksum( Int32ul, lambda data: binascii.crc32(data), this._._.bin.data ), 'date' / PaddedString(16, 'ascii'), 'title' / PaddedString(25, 'ascii'), 'description' / PaddedString(129, 'ascii'), 'version' / PaddedString(17, 'ascii'), 'author' / PaddedString(17, 'ascii'), Const(b'BLITTYPE'), 'category' / PaddedString(17, 'ascii'), 'url' / PaddedString(129, 'ascii'), 'filetypes' / PrefixedArray(Int8ul, PaddedString(5, 'ascii')), 'icon' / struct_blit_image, 'splash' / struct_blit_image )) ) struct_blit_meta_standalone = Struct( 'header' / Const(b'BLITMETA'), 'data' / Prefixed(Int16ul, Struct( 'checksum' / Int32ul, 'date' / PaddedString(16, 'ascii'),
"int8": Int8ub, "int16": Int16ub, "int32": Int32ub, "string": FocusedSeq( 0, PascalString(ExprAdapter(Int32ub, encoder=lambda obj, ctx: obj // 2 + 1, decoder=lambda obj, ctx: (obj - 1) * 2), encoding="utf-16-be"), Padding(2)), "binary": Prefixed(Int32ub, GreedyBytes) # parses to byte string })) class DBFieldFixedAdapter(Adapter): def __init__(self, subcon, ftype): self.ftype = ftype super().__init__(subcon) def _encode(self, obj, context): return {"type": self.ftype, "value": obj} def _decode(self, obj, context): if obj["type"] != self.ftype: raise TypeError("Parsed type {} but expected {}".format( obj["type"], self.ftype))
) CompressionMethods = "compression_methods" / Struct( "length" / Int8ub, # TODO: Reject packets of length 0 Array(lambda ctx: ctx.length, "compression_methods" / Int8ub), ) ServerName = Struct( "type" / Int8ub, "name" / PascalString("length" / Int16ub), ) SNIExtension = Prefixed( Int16ub, Struct( Int16ub, "server_names" / GreedyRange("server_name" / Struct( "name_type" / Int8ub, "host_name" / PascalString("length" / Int16ub), )))) ALPNExtension = Prefixed( Int16ub, Struct( Int16ub, "alpn_protocols" / GreedyRange("name" / PascalString(Int8ub), ), )) UnknownExtension = Struct("bytes" / PascalString("length" / Int16ub)) Extension = "Extension" / Struct( "type" / Int16ub,
return FormatFieldNoNan("<", "d") def Vec(subcon: Construct) -> Array: # noqa: N802 """Dynamic sized array. Args: subcon (Construct): the type of the array members. Returns: Array: a Construct PrefixedArray. """ return PrefixedArray(U32, subcon) Bytes = Prefixed(U32, GreedyBytes) class _String(Adapter): def __init__(self) -> None: super().__init__(Bytes) # type: ignore def _decode(self, obj: bytes, context, path) -> str: return obj.decode("utf8") def _encode(self, obj: str, context, path) -> bytes: return bytes(obj, "utf8") String = _String()
"uid" / Default(Int32ub, 0), "gid" / Default(Int32ub, 0), "gids" / Default(Int32ub, 0) # should be length-prefixed array? ) RpcAuthShort = Pass RpcAuthDes = Pass RpcOpaqueAuth = Struct( "flavor" / Default(RpcAuthFlavor, "null"), Embedded( Prefixed( Int32ub, Switch( this.flavor, { "null": Pass, "unix": RpcAuthUnix, "short": RpcAuthShort, "des": RpcAuthDes })))) PortmapPort = 111 PortmapVersion = 2 PortmapProcedure = Enum(Int32ub, null=0, set=1, unset=2, getport=3, dump=4, call_result=5)
def compute_header_hmac_hash(context): """Compute HMAC-SHA256 hash of header. Used to prevent header tampering.""" return hmac.new( hashlib.sha512(b'\xff' * 8 + hashlib.sha512( context._.header.value.dynamic_header.master_seed.data + context.transformed_key + b'\x01').digest()).digest(), context._.header.data, hashlib.sha256).digest() # --------------- KDF Params / Plugin Data ---------------- VariantDictionaryItem = Struct( "type" / Byte, "key" / Prefixed(Int32ul, GreedyString('utf-8')), "value" / Prefixed( Int32ul, Switch( this.type, { 0x04: Int32ul, 0x05: Int64ul, 0x08: Flag, 0x0C: Int32sl, 0x0D: Int64sl, 0x42: GreedyBytes, 0x18: GreedyString('utf-8') })), "next_byte" / Peek(Byte)) # new dynamic dictionary structure added in KDBX4 VariantDictionary = Struct(
Terminated) SshAgentLock = SshAgentUnlock = FocusedSeq('passphrase', 'passphrase' / SshBytes, Terminated) # define and parse the size field separately in order to have a way to know how # many bytes to expect to read on the socket. SshAgentRequestHeader = Int32ub SshAgentRequest = FocusedSeq( 'request', 'request' / Prefixed( Int32ub, Struct( 'code' / PyEnum(Byte, SshAgentRequestCode), 'message' / Switch(this.code, { SshAgentRequestCode.REQUEST_IDENTITIES: Terminated, SshAgentRequestCode.ADD_IDENTITY: SshAddIdentity, SshAgentRequestCode.REMOVE_IDENTITY: SshRemoveIdentity, SshAgentRequestCode.REMOVE_ALL_IDENTITIES: Terminated, SshAgentRequestCode.SIGN_REQUEST: SshAgentSignRequest, SshAgentRequestCode.LOCK: SshAgentLock, SshAgentRequestCode.UNLOCK: SshAgentUnlock, }, default=GreedyBytes), Terminated)), Terminated) SshAgentResponse = FocusedSeq( 'response', 'response' / Prefixed( Int32ub, Struct( 'code' / PyEnum(Byte, SshAgentResponseCode), 'message' / Switch(this.code, { SshAgentResponseCode.FAILURE: Terminated, SshAgentResponseCode.SUCCESS: Terminated,