forked from nemiah/phpFinTS
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDialogInitialization.php
More file actions
239 lines (214 loc) · 9.55 KB
/
DialogInitialization.php
File metadata and controls
239 lines (214 loc) · 9.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
<?php
namespace Fhp\Protocol;
use Fhp\BaseAction;
use Fhp\Model\NoPsd2TanMode;
use Fhp\Model\TanMode;
use Fhp\Options\Credentials;
use Fhp\Options\FinTsOptions;
use Fhp\Segment\HISYN\HISYNv4;
use Fhp\Segment\HKIDN\HKIDNv2;
use Fhp\Segment\HKSYN\HKSYNv3;
use Fhp\Segment\HKVVB\HKVVBv3;
use Fhp\Segment\TAN\HKTANFactory;
/**
* Initializes a FinTs dialog. The dialog initialization message is usually the first message that should be sent over
* the wire. The most basic form consists of HIKDN for authentication, and HKVVB to declare the current BPD/UPD versions
* present at the client. The server responds with updated BPD/UPD data.
* @link https://www.hbci-zka.de/dokumente/spezifikation_deutsch/fintsv3/FinTS_3.0_Formals_2017-10-06_final_version.pdf
* Section: C.3
*
* An extended form of dialog initialization (called "synchronization") additionally requests a new Kundensystem-ID by
* sending a HKSYN segment. The Kundensystem-ID identifies the application using the phpFinTS library plus the device on
* which it runs.
* This action automatically executes synchroniziation if `$kundensystemId=null` was passed to the constructor. In this
* case, the opened dialog must not be used for any other (financial/business) actions, so the caller must call
* {@link FinTs::endDialog()} immediately after executing a {@link DialogInitialization} without pre-existing
* Kundensystem-ID.
* @link https://www.hbci-zka.de/dokumente/spezifikation_deutsch/fintsv3/FinTS_3.0_Formals_2017-10-06_final_version.pdf
* Section: C.8
*
* By default (`$hktanRef = 'HKIDN'`), the dialog is opened with strong authentication (PSD2), which requires that the
* $tanMode has already been selected.
* For special PIN/TAN management use cases (e.g. enumerating the available TAN media) that need to be executable
* without strong authentication, or for synchronization (see above), the `$hktanRef` can be set to the segment
* identifier of the PIN/TAN management transaction or null, respectively, to indicate weak authentication.
* @link https://www.hbci-zka.de/dokumente/spezifikation_deutsch/fintsv3/FinTS_3.0_Security_Sicherheitsverfahren_PINTAN_2018-02-23_final_version.pdf
* Section: B.4.3.1 and B.4.3.3
*
* Rough overview of the initialization procedure with no prior information on the client side:
* 1. Open connection.
* 2. Initialize and close anonymously to retrieve BPD (HITANS, HIPINS, ...). This is implemented in
* {@link FinTs::ensureBpdAvailable()}.
* 3. Initialize a dialog with $kundensystemId=null ("synchronization") and $hktanRef=null, and close it again. At this
* point, the allowed TAN modes are available, so the user can select a $tanMode.
* 4. Optional: If the user needs to select a TAN medium, initialize another dialog with $hktanRef=HKTAB to execute
* {@link GetTanMedia}, and close it again. At this point, both $tanMode and $tanMedium are available.
* 4. Initialize a strongly authenticated dialog (which possibly requires a TAN) to retrieve UPD.
* 5. Now we're ready to execute business transactions.
* Note that steps (2.) and (3.) can be skipped if the BPD/Kundensystem-ID are already present on the client side.
*/
class DialogInitialization extends BaseAction
{
// These come from FinTs and are needed as inputs for the dialog initialization. They are NOT available after
// serialization, i.e. not in processResponse().
/** @var FinTsOptions */
private $options;
/** @var Credentials */
private $credentials;
/** @var TanMode|null */
private $tanMode;
/** @var string|null */
private $tanMedium;
/**
* The segment that HKTAN points to. This implicitly defines what kind of dialog is initialized: null means weak
* authentication, 'HKIDN' means strong authentication and other values initialize a special PIN/TAN dialog.
* @var string
*/
private $hktanRef;
// This is the persistent state of the dialog initialization (can be both input and output).
/** @var string|null */
private $kundensystemId; // May be present initially. If not, will send HKSYN to obtain it.
/** @var int|null */
private $messageNumber; // Stored temporarily, to continue properly after TAN input.
/** @var string|null */
private $dialogId; // This is the main result.
// Side results.
/** @var UPD|null */
private $upd;
/**
* @param TanMode|null $tanMode The TAN mode selected by the user.
* @param string|null $tanMedium Possibly a TAN medium selected by the user.
* @param string|null $kundensystemId The current Kundensystem-ID, if the client already has one.
* @param string|null $hktanRef The segment to declare inside HKTAN.
* If this is null, a weakly authenticated dialog (with the TAN mode 999) will be initialized, which can only be
* used for synchronization and/or to retrieve BPD.
* If this is 'HKIDN', a regular, strongly authenticated dialog will be initialized, which may require a TAN.
* If it is one of the special PIN/TAN management segments (e.g. HKTAB), then the dialog does not have strong
* authentication (no TAN required) and can only be used for that one particular transaction.
*/
public function __construct(FinTsOptions $options, Credentials $credentials, ?TanMode $tanMode, ?string $tanMedium, ?string $kundensystemId, ?string $hktanRef = 'HKIDN')
{
if ($hktanRef !== null && $tanMode === null) {
throw new \InvalidArgumentException('hktanRef is ignored unless a tanMode is given');
}
$this->options = $options;
$this->credentials = $credentials;
$this->tanMode = $tanMode instanceof NoPsd2TanMode ? null : $tanMode;
$this->tanMedium = $tanMedium;
$this->kundensystemId = $kundensystemId;
$this->hktanRef = $hktanRef;
}
/**
* @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023
*/
public function serialize(): string
{
return serialize($this->__serialize());
}
public function __serialize(): array
{
return [
parent::__serialize(),
$this->hktanRef,
$this->kundensystemId,
$this->messageNumber,
$this->dialogId,
];
}
/**
* @deprecated Beginning from PHP7.4 __unserialize is used for new generated strings, then this method is only used for previously generated strings - remove after May 2023
*
* @param string $serialized
* @return void
*/
public function unserialize($serialized)
{
self::__unserialize(unserialize($serialized));
}
public function __unserialize(array $serialized): void
{
list(
$parentSerialized,
$this->hktanRef,
$this->kundensystemId,
$this->messageNumber,
$this->dialogId,
) = $serialized;
is_array($parentSerialized) ?
parent::__unserialize($parentSerialized) :
parent::unserialize($parentSerialized);
}
protected function createRequest(BPD $bpd, ?UPD $upd)
{
throw new \AssertionError('DialogInitialization::createRequest should not be used.');
}
/**
* @param BPD|null $bpd The BPD. Note that we support null here because a dialog initialization is how the BPD can
* be obtained in the first place.
* @param UPD|null $upd The UPD.
* @return array|\Fhp\Segment\BaseSegment|\Fhp\Segment\BaseSegment[]
*/
public function getNextRequest(?BPD $bpd, ?UPD $upd)
{
$request = [
HKIDNv2::create($this->options->bankCode, $this->credentials, $this->kundensystemId ?? '0'),
HKVVBv3::create($this->options, $bpd, $upd),
];
if ($this->tanMode !== null) {
$request[] = HKTANFactory::createProzessvariante2Step1(
$this->tanMode, $this->tanMedium, $this->hktanRef ?? 'HKIDN');
}
if ($this->kundensystemId === null) {
// NOTE: HKSYN must be *after* HKTAN.
$request[] = HKSYNv3::createEmpty(); // See section C.8.1.1
}
return $request;
}
public function processResponse(Message $response)
{
parent::processResponse($response);
$this->dialogId = $response->header->dialogId;
if ($this->kundensystemId === null) {
/** @var HISYNv4 $hisyn */
$hisyn = $response->requireSegment(HISYNv4::class);
if ($hisyn->kundensystemId === null) {
throw new UnexpectedResponseException('No Kundensystem-ID received');
}
$this->kundensystemId = $hisyn->kundensystemId;
}
if (UPD::containedInResponse($response)) {
$this->upd = UPD::extractFromResponse($response);
}
}
public function isStronglyAuthenticated(): bool
{
return $this->hktanRef === 'HKIDN';
}
public function getKundensystemId(): ?string
{
return $this->kundensystemId;
}
public function getMessageNumber(): ?int
{
return $this->messageNumber;
}
public function setMessageNumber(?int $messageNumber): void
{
$this->messageNumber = $messageNumber;
}
public function getDialogId(): ?string
{
return $this->dialogId;
}
/**
* To be called when a TAN is required for login, but we need to intermittently store the Dialog-ID.
*/
public function setDialogId(?string $dialogId): void
{
$this->dialogId = $dialogId;
}
public function getUpd(): ?UPD
{
return $this->upd;
}
}