From: Henryk Plötz Date: Sun, 19 Aug 2018 23:15:23 +0000 (+0200) Subject: (Re-)Add one step PIN mechanism X-Git-Tag: v2.0.0~1^2~100 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f5e2db4ed069b7ab42eafa41a88ba2acf0584d17;p=thirdparty%2Fpython-fints.git (Re-)Add one step PIN mechanism --- diff --git a/fints/client.py b/fints/client.py index 7bd9df0..00aee66 100644 --- a/fints/client.py +++ b/fints/client.py @@ -10,6 +10,7 @@ from .connection import FinTSHTTPSConnection from .dialog import FinTSDialogOLD, FinTSDialog from .formals import TwoStepParametersCommon from .message import FinTSMessageOLD +from .security import PinTanDummyEncryptionMechanism, PinTanOneStepAuthenticationMechanism from .models import ( SEPAAccount, TANChallenge, TANChallenge3, TANChallenge4, TANChallenge5, TANChallenge6, @@ -47,6 +48,8 @@ class FinTS3Client: self.bpa = None self.bpd = [] self.upd_version = 0 + self.upa = None + self.upd = [] self.product_name = 'pyfints' self.product_version = '0.2' @@ -70,8 +73,13 @@ class FinTS3Client: ) ) - for seg in message.find_segments(HIUPA4): - self.upd_version = seg.upd_version + upa = message.find_segment_first(HIUPA4) + if upa: + self.upa = upa + self.upd_version = upa.upd_version + self.upd = list( + message.find_segments('HIUPD') + ) def find_bpd(self, type): for seg in self.bpd: @@ -209,28 +217,6 @@ class FinTS3Client: if seg: return seg.balance_booked.as_mt940_Balance() - def _create_balance_message(self, dialog: FinTSDialogOLD, account: SEPAAccount): - hversion = dialog.hksalversion - - if hversion in (1, 2, 3, 4, 5, 6): - acc = ':'.join([ - account.accountnumber, account.subaccount or '', str(280), account.blz - ]) - elif hversion == 7: - acc = ':'.join([ - account.iban, account.bic, account.accountnumber, account.subaccount or '', str(280), account.blz - ]) - else: - raise ValueError('Unsupported HKSAL version {}'.format(hversion)) - - return self._new_message(dialog, [ - HKSAL( - 3, - hversion, - acc - ) - ]) - def get_holdings(self, account: SEPAAccount): """ Retrieve holdings of an account. @@ -520,11 +506,11 @@ class FinTS3PinTanClient(FinTS3Client): if not lazy_init: self._ensure_system_id() - return FinTSDialog(self, lazy_init=lazy_init) - - # FIXME - # dialog = FinTSDialogOLD(self.blz, self.username, self.pin, self.systemid, self.connection) - # return dialog + return FinTSDialog(self, + lazy_init=lazy_init, + enc_mechanism=PinTanDummyEncryptionMechanism(1), + auth_mechanisms=[PinTanOneStepAuthenticationMechanism(self.pin)], + ) def _new_message(self, dialog: FinTSDialogOLD, segments, tan=None): return FinTSMessageOLD(self.blz, self.username, self.pin, dialog.systemid, dialog.dialogid, dialog.msgno, diff --git a/fints/dialog.py b/fints/dialog.py index f6f358d..8092457 100644 --- a/fints/dialog.py +++ b/fints/dialog.py @@ -16,12 +16,12 @@ class FinTSDialogError(Exception): class FinTSDialog: - def __init__(self, client=None, lazy_init=False): + def __init__(self, client=None, lazy_init=False, enc_mechanism=None, auth_mechanisms=[]): self.client = client self.next_message_number = dict((v, 1) for v in MessageDirection) self.messages = dict((v, {}) for v in MessageDirection) - self.auth_mechanisms = [] - self.enc_mechanism = None + self.auth_mechanisms = auth_mechanisms + self.enc_mechanism = enc_mechanism self.open = False self.need_init = True self.lazy_init = lazy_init @@ -91,7 +91,7 @@ class FinTSDialog: response = self.client.connection.send(message) ##assert response.segments[0].message_number == self.next_message_number[response.DIRECTION] - # FIXME Better handling + # FIXME Better handling of HKEND in exception case self.messages[response.segments[0].message_number] = message self.next_message_number[response.DIRECTION] += 1 @@ -121,12 +121,12 @@ class FinTSDialog: return message def finish_message(self, message): - message += HNHBS1(message.segments[0].message_number) - # Create signature(s) in reverse order: from inner to outer for auth_mech in reversed(self.auth_mechanisms): auth_mech.sign_commit(message) + message += HNHBS1(message.segments[0].message_number) + if self.enc_mechanism: self.enc_mechanism.encrypt(message) diff --git a/fints/fields.py b/fints/fields.py index 39e6400..ef6dad8 100644 --- a/fints/fields.py +++ b/fints/fields.py @@ -333,6 +333,8 @@ class DateField(FixedLengthMixin, NumericField): _FIXED_LENGTH = [8] def _parse_value(self, value): + if isinstance(value, datetime.date): + return value val = super()._parse_value(value) val = str(val) return datetime.date(int(val[0:4]), int(val[4:6]), int(val[6:8])) @@ -348,6 +350,8 @@ class TimeField(FixedLengthMixin, DigitsField): _FIXED_LENGTH = [6] def _parse_value(self, value): + if isinstance(value, datetime.time): + return value val = super()._parse_value(value) return datetime.time(int(val[0:2]), int(val[2:4]), int(val[4:6])) diff --git a/fints/security.py b/fints/security.py new file mode 100644 index 0000000..dd2b685 --- /dev/null +++ b/fints/security.py @@ -0,0 +1,150 @@ +import datetime, random + +from .message import FinTSMessage +from .types import SegmentSequence +from .formals import SecurityProfile, SecurityRole, IdentifiedRole, DateTimeType, UsageEncryption, SecurityIdentificationDetails, SecurityDateTime, EncryptionAlgorithm, KeyName, CompressionFunction, OperationMode, EncryptionAlgorithmCoded, AlgorithmParameterName, AlgorithmParameterIVName, KeyType, SecurityMethod, SecurityApplicationArea, UserDefinedSignature, HashAlgorithm, SignatureAlgorithm, UserDefinedSignature +from .segments.message import HNVSK3, HNVSD1, HNSHK4, HNSHA2 + +class EncryptionMechanism: + def encrypt(self, message: FinTSMessage): + raise NotImplemented() + + def decrypt(self, message: FinTSMessage): + raise NotImplemented() + +class AuthenticationMechanism: + def sign_prepare(self, message: FinTSMessage): + raise NotImplemented() + + def sign_commit(self, message: FinTSMessage): + raise NotImplemented() + + def verify(self, message: FinTSMessage): + raise NotImplemented() + +class PinTanDummyEncryptionMechanism(EncryptionMechanism): + def __init__(self, security_method_version=1): + super().__init__() + self.security_method_version = security_method_version + + def encrypt(self, message: FinTSMessage): + assert message.segments[0].header.type == 'HNHBK' + assert message.segments[-1].header.type == 'HNHBS' + + plain_segments = message.segments[1:-1] + del message.segments[1:-1] + + _now = datetime.datetime.now() + + message.segments.insert(1, + HNVSK3( + security_profile=SecurityProfile(SecurityMethod.PIN, self.security_method_version), + security_function='998', + security_role=SecurityRole.ISS, + security_identification_details=SecurityIdentificationDetails( + IdentifiedRole.MS, + identifier=message.dialog.client.system_id, + ), + security_datetime=SecurityDateTime( + DateTimeType.STS, + _now.date(), + _now.time(), + ), + encryption_algorithm=EncryptionAlgorithm( + UsageEncryption.OSY, + OperationMode.CBC, + EncryptionAlgorithmCoded.TWOKEY3DES, + b'\x00'*8, + AlgorithmParameterName.KYE, + AlgorithmParameterIVName.IVC, + ), + key_name=KeyName( + message.dialog.client.bank_identifier, + message.dialog.client.user_id, + KeyType.V, + 0, + 0, + ), + compression_function=CompressionFunction.NULL, + ) + ) + message.segments[1].header.number = 998 + message.segments.insert(2, + HNVSD1( + data=SegmentSequence(segments=plain_segments) + ) + ) + message.segments[2].header.number = 999 + + def decrypt(self, message: FinTSMessage): + pass + + +class PinTanOneStepAuthenticationMechanism(AuthenticationMechanism): + def __init__(self, pin, tan=None): + self.pin=pin + self.tan=tan + self.pending_signature=None + + def sign_prepare(self, message: FinTSMessage): + _now = datetime.datetime.now() + rand = random.SystemRandom() + + self.pending_signature = HNSHK4( + security_profile = SecurityProfile(SecurityMethod.PIN, 1), + security_function = '999', + security_reference = rand.randint(1000000, 9999999), + security_application_area = SecurityApplicationArea.SHM, + security_role = SecurityRole.ISS, + security_identification_details = SecurityIdentificationDetails( + IdentifiedRole.MS, + identifier=message.dialog.client.system_id, + ), + security_reference_number = 1, ## FIXME + security_datetime = SecurityDateTime( + DateTimeType.STS, + _now.date(), + _now.time(), + ), + hash_algorithm = HashAlgorithm( + usage_hash = '1', + hash_algorithm = '999', + algorithm_parameter_name = '1', + ), + signature_algorithm = SignatureAlgorithm( + usage_signature = '6', + signature_algorithm = '10', + operation_mode = '16', + ), + key_name = KeyName( + message.dialog.client.bank_identifier, + message.dialog.client.user_id, + KeyType.S, + 0, + 0, + ), + + ) + + message += self.pending_signature + + def sign_commit(self, message: FinTSMessage): + if not self.pending_signature: + raise Error("No signature is pending") + + if self.pending_signature not in message.segments: + raise Error("Cannot sign a message that was not prepared") + + signature = HNSHA2( + security_reference = self.pending_signature.security_reference, + user_defined_signature = UserDefinedSignature( + pin=self.pin, + tan=self.tan, + ), + ) + + self.pending_signature = None + message += signature + + def verify(self, message: FinTSMessage): + pass diff --git a/fints/segments/__init__.py b/fints/segments/__init__.py index 9d66c60..4f018fa 100644 --- a/fints/segments/__init__.py +++ b/fints/segments/__init__.py @@ -82,43 +82,6 @@ class FinTS3Segment(Container, SubclassesMixin, metaclass=FinTS3SegmentMeta): return target_cls -class HNVSD1(FinTS3Segment): - "Verschlüsselte Daten" - data = SegmentSequenceField(_d="Daten, verschlüsselt") - -class HNVSK3(FinTS3Segment): - "Verschlüsselungskopf" - security_profile = DataElementGroupField(type=SecurityProfile, _d="Sicherheitsprofil") - security_function = DataElementField(type='code', max_length=3, _d="Sicherheitsfunktion, kodiert") - security_role = CodeField(SecurityRole, max_length=3, _d="Rolle des Sicherheitslieferanten, kodiert") - security_identification_details = DataElementGroupField(type=SecurityIdentificationDetails, _d="Sicherheitsidentifikation, Details") - security_datetime = DataElementGroupField(type=SecurityDateTime, _d="Sicherheitsdatum und -uhrzeit") - encryption_algorithm = DataElementGroupField(type=EncryptionAlgorithm, _d="Verschlüsselungsalgorithmus") - key_name = DataElementGroupField(type=KeyName, _d="Schlüsselname") - compression_function = CodeField(CompressionFunction, max_length=3, _d="Komprimierungsfunktion") - certificate = DataElementGroupField(type=Certificate, required=False, _d="Zertifikat") - -class HNSHK4(FinTS3Segment): - "Signaturkopf" - security_profile = DataElementGroupField(type=SecurityProfile, _d="Sicherheitsprofil") - security_function = DataElementField(type='code', max_length=3, _d="Sicherheitsfunktion, kodiert") - security_reference = DataElementField(type='an', max_length=14, _d="Sicherheitskontrollreferenz") - security_application_area = CodeField(SecurityApplicationArea, max_length=3, _d="Bereich der Sicherheitsapplikation, kodiert") - security_role = CodeField(SecurityRole, max_length=3, _d="Rolle des Sicherheitslieferanten, kodiert") - security_identification_details = DataElementGroupField(type=SecurityIdentificationDetails, _d="Sicherheitsidentifikation, Details") - security_reference_number = DataElementField(type='num', max_length=16, _d="Sicherheitsreferenznummer") - security_datetime = DataElementGroupField(type=SecurityDateTime, _d="Sicherheitsdatum und -uhrzeit") - hash_algorithm = DataElementGroupField(type=HashAlgorithm, _d="Hashalgorithmus") - signature_algorithm = DataElementGroupField(type=SignatureAlgorithm, _d="Signaturalgorithmus") - key_name = DataElementGroupField(type=KeyName, _d="Schlüsselname") - certificate = DataElementGroupField(type=Certificate, required=False, _d="Zertifikat") - -class HNSHA2(FinTS3Segment): - "Signaturabschluss" - security_reference = DataElementField(type='an', max_length=14, _d="Sicherheitskontrollreferenz") - validation_result = DataElementField(type='bin', max_length=512, required=False, _d="Validierungsresultat") - user_defined_signature = DataElementGroupField(type=UserDefinedSignature, required=False, _d="Benutzerdefinierte Signatur") - class HIRMG2(FinTS3Segment): "Rückmeldungen zur Gesamtnachricht" responses = DataElementGroupField(type=Response, min_count=1, max_count=99, _d="Rückmeldung") diff --git a/fints/segments/message.py b/fints/segments/message.py index 8180184..71cb1f9 100644 --- a/fints/segments/message.py +++ b/fints/segments/message.py @@ -3,8 +3,8 @@ import time from fints.utils import fints_escape from . import FinTS3SegmentOLD, FinTS3Segment -from fints.formals import ReferenceMessage -from fints.fields import DataElementGroupField, DataElementField, ZeroPaddedNumericField +from fints.formals import ReferenceMessage, SecurityProfile, SecurityRole, SecurityIdentificationDetails, SecurityDateTime, EncryptionAlgorithm, KeyName, CompressionFunction, Certificate, SecurityApplicationArea, HashAlgorithm, SignatureAlgorithm, UserDefinedSignature +from fints.fields import DataElementGroupField, DataElementField, ZeroPaddedNumericField, CodeField, SegmentSequenceField class HNHBK3(FinTS3Segment): "Nachrichtenkopf" @@ -157,3 +157,49 @@ class HNHBS(FinTS3SegmentOLD): str(msgno) ] super().__init__(segno, data) + +class HNVSK3(FinTS3Segment): + """Verschlüsselungskopf, version 3 + + Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" + security_profile = DataElementGroupField(type=SecurityProfile, _d="Sicherheitsprofil") + security_function = DataElementField(type='code', max_length=3, _d="Sicherheitsfunktion, kodiert") + security_role = CodeField(SecurityRole, max_length=3, _d="Rolle des Sicherheitslieferanten, kodiert") + security_identification_details = DataElementGroupField(type=SecurityIdentificationDetails, _d="Sicherheitsidentifikation, Details") + security_datetime = DataElementGroupField(type=SecurityDateTime, _d="Sicherheitsdatum und -uhrzeit") + encryption_algorithm = DataElementGroupField(type=EncryptionAlgorithm, _d="Verschlüsselungsalgorithmus") + key_name = DataElementGroupField(type=KeyName, _d="Schlüsselname") + compression_function = CodeField(CompressionFunction, max_length=3, _d="Komprimierungsfunktion") + certificate = DataElementGroupField(type=Certificate, required=False, _d="Zertifikat") + +class HNVSD1(FinTS3Segment): + """Verschlüsselte Daten, version 1 + + Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" + data = SegmentSequenceField(_d="Daten, verschlüsselt") + +class HNSHK4(FinTS3Segment): + """Signaturkopf, version 4 + + Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" + security_profile = DataElementGroupField(type=SecurityProfile, _d="Sicherheitsprofil") + security_function = DataElementField(type='code', max_length=3, _d="Sicherheitsfunktion, kodiert") + security_reference = DataElementField(type='an', max_length=14, _d="Sicherheitskontrollreferenz") + security_application_area = CodeField(SecurityApplicationArea, max_length=3, _d="Bereich der Sicherheitsapplikation, kodiert") + security_role = CodeField(SecurityRole, max_length=3, _d="Rolle des Sicherheitslieferanten, kodiert") + security_identification_details = DataElementGroupField(type=SecurityIdentificationDetails, _d="Sicherheitsidentifikation, Details") + security_reference_number = DataElementField(type='num', max_length=16, _d="Sicherheitsreferenznummer") + security_datetime = DataElementGroupField(type=SecurityDateTime, _d="Sicherheitsdatum und -uhrzeit") + hash_algorithm = DataElementGroupField(type=HashAlgorithm, _d="Hashalgorithmus") + signature_algorithm = DataElementGroupField(type=SignatureAlgorithm, _d="Signaturalgorithmus") + key_name = DataElementGroupField(type=KeyName, _d="Schlüsselname") + certificate = DataElementGroupField(type=Certificate, required=False, _d="Zertifikat") + +class HNSHA2(FinTS3Segment): + """Signaturabschluss, version 2 + + Source: FinTS Financial Transaction Services, Sicherheitsverfahren HBCI""" + security_reference = DataElementField(type='an', max_length=14, _d="Sicherheitskontrollreferenz") + validation_result = DataElementField(type='bin', max_length=512, required=False, _d="Validierungsresultat") + user_defined_signature = DataElementGroupField(type=UserDefinedSignature, required=False, _d="Benutzerdefinierte Signatur") + diff --git a/tests/test_message_serializer.py b/tests/test_message_serializer.py index 661fedb..4f585ed 100644 --- a/tests/test_message_serializer.py +++ b/tests/test_message_serializer.py @@ -63,6 +63,6 @@ def test_escape(): def test_serialize_2(): from fints.formals import SegmentSequence import fints.formals, fints.segments - s = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0'), fints.segments.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1), data=SegmentSequence([fints.segments.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1'), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0)), fints.segments.HIRMG2(header=fints.formals.SegmentHeader('HIRMG', 3, 2), responses=[fints.formals.Response(code='0010', reference_element=None, text='Nachricht entgegengenommen.'), fints.formals.Response(code='0100', reference_element=None, text='Dialog beendet.')]), fints.segments.HNSHA2(header=fints.formals.SegmentHeader('HNSHA', 4, 2), security_reference='9166926')])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1), message_number=2)]) + s = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0'), fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1), data=SegmentSequence([fints.segments.message.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1'), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1'), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0)), fints.segments.HIRMG2(header=fints.formals.SegmentHeader('HIRMG', 3, 2), responses=[fints.formals.Response(code='0010', reference_element=None, text='Nachricht entgegengenommen.'), fints.formals.Response(code='0100', reference_element=None, text='Dialog beendet.')]), fints.segments.message.HNSHA2(header=fints.formals.SegmentHeader('HNSHA', 4, 2), security_reference='9166926')])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1), message_number=2)]) assert FinTS3Serializer().serialize_message(s) == TEST_MESSAGES['basic_simple'] diff --git a/tests/test_models.py b/tests/test_models.py index 10fa2f6..7ca3010 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -120,7 +120,7 @@ def test_find_subclass(): def test_nested_output_evalable(): import fints.segments, fints.formals - a = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3, None), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1', algorithm_parameter_iv_value=None), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0', certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1, None), data=SegmentSequence([fints.segments.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1', algorithm_parameter_value=None), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.FinTS3Segment(header=fints.formals.SegmentHeader('HIRMG', 3, 2, None), _additional_data=[['0010', None, 'Nachricht entgegengenommen.'], ['0100', None, 'Dialog beendet.']]), fints.segments.FinTS3Segment(header=fints.formals.SegmentHeader('HNSHA', 4, 2, None), _additional_data=['9166926'])])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1, None), message_number=2)]) + a = SegmentSequence([fints.segments.message.HNHBK3(header=fints.formals.SegmentHeader('HNHBK', 1, 3, None), message_size='000000000428', hbci_version=300, dialogue_id='430711670077=043999659571CN9D=', message_number=2, reference_message=fints.formals.ReferenceMessage(dialogue_id='430711670077=043999659571CN9D=', message_number=2)), fints.segments.message.HNVSK3(header=fints.formals.SegmentHeader('HNVSK', 998, 3, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='998', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), encryption_algorithm=fints.formals.EncryptionAlgorithm(usage_encryption='2', operation_mode='2', encryption_algorithm='13', algorithm_parameter_value=b'00000000', algorithm_parameter_name='5', algorithm_parameter_iv_name='1', algorithm_parameter_iv_value=None), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), compression_function='0', certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.message.HNVSD1(header=fints.formals.SegmentHeader('HNVSD', 999, 1, None), data=SegmentSequence([fints.segments.message.HNSHK4(header=fints.formals.SegmentHeader('HNSHK', 2, 4, None), security_profile=fints.formals.SecurityProfile(security_method='PIN', security_method_version=1), security_function='999', security_reference='9166926', security_application_area='1', security_role='1', security_identification_details=fints.formals.SecurityIdentificationDetails(identified_role='2', cid=None, identifier='oIm3BlHv6mQBAADYgbPpp+kWrAQA'), security_reference_number=1, security_datetime=fints.formals.SecurityDateTime(date_time_type='1', date=None, time=None), hash_algorithm=fints.formals.HashAlgorithm(usage_hash='1', hash_algorithm='999', algorithm_parameter_name='1', algorithm_parameter_value=None), signature_algorithm=fints.formals.SignatureAlgorithm(usage_signature='6', signature_algorithm='10', operation_mode='16'), key_name=fints.formals.KeyName(bank_identifier=fints.formals.BankIdentifier(country_identifier='280', bank_code='15050500'), user_id='hermes', key_type='S', key_number=0, key_version=0), certificate=fints.formals.Certificate(certificate_type=None, certificate_content=None)), fints.segments.FinTS3Segment(header=fints.formals.SegmentHeader('HIRMG', 3, 2, None), _additional_data=[['0010', None, 'Nachricht entgegengenommen.'], ['0100', None, 'Dialog beendet.']]), fints.segments.FinTS3Segment(header=fints.formals.SegmentHeader('HNSHA', 4, 2, None), _additional_data=['9166926'])])), fints.segments.message.HNHBS1(header=fints.formals.SegmentHeader('HNHBS', 5, 1, None), message_number=2)]) output = StringIO() a.print_nested(stream=output)