node-opcua-server 2.165.2 → 2.167.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base_server.js +1 -3
- package/dist/base_server.js.map +1 -1
- package/dist/extract_password_from_blob.d.ts +29 -0
- package/dist/extract_password_from_blob.js +55 -0
- package/dist/extract_password_from_blob.js.map +1 -0
- package/dist/opcua_server.js +6 -6
- package/dist/opcua_server.js.map +1 -1
- package/dist/register_server_manager.d.ts +8 -8
- package/dist/register_server_manager.js +40 -40
- package/dist/register_server_manager.js.map +1 -1
- package/dist/server_end_point.d.ts +2 -2
- package/dist/server_end_point.js +5 -12
- package/dist/server_end_point.js.map +1 -1
- package/package.json +42 -41
- package/source/base_server.ts +2 -4
- package/source/extract_password_from_blob.ts +74 -0
- package/source/opcua_server.ts +21 -15
- package/source/register_server_manager.ts +75 -72
- package/source/server_end_point.ts +16 -23
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-opcua-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.167.0",
|
|
4
4
|
"description": "pure nodejs OPCUA SDK - module server",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsc -b",
|
|
7
7
|
"lint": "eslint source test",
|
|
8
8
|
"format": "prettier --write source test",
|
|
9
9
|
"clean": "npx rimraf -g node_modules dist *.tsbuildinfo certificates",
|
|
10
|
-
"test": "mocha"
|
|
10
|
+
"test": "mocha",
|
|
11
|
+
"test:check": "tsc --noEmit -p test/tsconfig.json"
|
|
11
12
|
},
|
|
12
13
|
"main": "./dist/index.js",
|
|
13
14
|
"types": "./dist/index.d.ts",
|
|
@@ -19,53 +20,53 @@
|
|
|
19
20
|
"chalk": "4.1.2",
|
|
20
21
|
"dequeue": "^1.0.5",
|
|
21
22
|
"lodash": "4.17.23",
|
|
22
|
-
"node-opcua-address-space": "2.
|
|
23
|
-
"node-opcua-address-space-base": "2.
|
|
23
|
+
"node-opcua-address-space": "2.167.0",
|
|
24
|
+
"node-opcua-address-space-base": "2.167.0",
|
|
24
25
|
"node-opcua-assert": "2.164.0",
|
|
25
|
-
"node-opcua-basic-types": "2.
|
|
26
|
-
"node-opcua-binary-stream": "2.
|
|
27
|
-
"node-opcua-certificate-manager": "2.
|
|
28
|
-
"node-opcua-client": "2.
|
|
29
|
-
"node-opcua-common": "2.
|
|
26
|
+
"node-opcua-basic-types": "2.167.0",
|
|
27
|
+
"node-opcua-binary-stream": "2.167.0",
|
|
28
|
+
"node-opcua-certificate-manager": "2.167.0",
|
|
29
|
+
"node-opcua-client": "2.167.0",
|
|
30
|
+
"node-opcua-common": "2.167.0",
|
|
30
31
|
"node-opcua-constants": "2.157.0",
|
|
31
|
-
"node-opcua-crypto": "5.3.
|
|
32
|
-
"node-opcua-data-model": "2.
|
|
33
|
-
"node-opcua-data-value": "2.
|
|
34
|
-
"node-opcua-date-time": "2.
|
|
32
|
+
"node-opcua-crypto": "5.3.3",
|
|
33
|
+
"node-opcua-data-model": "2.167.0",
|
|
34
|
+
"node-opcua-data-value": "2.167.0",
|
|
35
|
+
"node-opcua-date-time": "2.167.0",
|
|
35
36
|
"node-opcua-debug": "2.165.0",
|
|
36
|
-
"node-opcua-extension-object": "2.
|
|
37
|
-
"node-opcua-factory": "2.
|
|
38
|
-
"node-opcua-hostname": "2.
|
|
39
|
-
"node-opcua-nodeid": "2.
|
|
37
|
+
"node-opcua-extension-object": "2.167.0",
|
|
38
|
+
"node-opcua-factory": "2.167.0",
|
|
39
|
+
"node-opcua-hostname": "2.167.0",
|
|
40
|
+
"node-opcua-nodeid": "2.167.0",
|
|
40
41
|
"node-opcua-nodesets": "2.163.1",
|
|
41
|
-
"node-opcua-numeric-range": "2.
|
|
42
|
+
"node-opcua-numeric-range": "2.167.0",
|
|
42
43
|
"node-opcua-object-registry": "2.165.0",
|
|
43
|
-
"node-opcua-pki": "6.
|
|
44
|
-
"node-opcua-secure-channel": "2.
|
|
45
|
-
"node-opcua-service-browse": "2.
|
|
46
|
-
"node-opcua-service-call": "2.
|
|
47
|
-
"node-opcua-service-discovery": "2.
|
|
48
|
-
"node-opcua-service-endpoints": "2.
|
|
49
|
-
"node-opcua-service-filter": "2.
|
|
50
|
-
"node-opcua-service-history": "2.
|
|
51
|
-
"node-opcua-service-node-management": "2.
|
|
52
|
-
"node-opcua-service-query": "2.
|
|
53
|
-
"node-opcua-service-read": "2.
|
|
54
|
-
"node-opcua-service-register-node": "2.
|
|
55
|
-
"node-opcua-service-secure-channel": "2.
|
|
56
|
-
"node-opcua-service-session": "2.
|
|
57
|
-
"node-opcua-service-subscription": "2.
|
|
58
|
-
"node-opcua-service-translate-browse-path": "2.
|
|
59
|
-
"node-opcua-service-write": "2.
|
|
60
|
-
"node-opcua-status-code": "2.
|
|
61
|
-
"node-opcua-transport": "2.
|
|
62
|
-
"node-opcua-types": "2.
|
|
44
|
+
"node-opcua-pki": "6.12.0",
|
|
45
|
+
"node-opcua-secure-channel": "2.167.0",
|
|
46
|
+
"node-opcua-service-browse": "2.167.0",
|
|
47
|
+
"node-opcua-service-call": "2.167.0",
|
|
48
|
+
"node-opcua-service-discovery": "2.167.0",
|
|
49
|
+
"node-opcua-service-endpoints": "2.167.0",
|
|
50
|
+
"node-opcua-service-filter": "2.167.0",
|
|
51
|
+
"node-opcua-service-history": "2.167.0",
|
|
52
|
+
"node-opcua-service-node-management": "2.167.0",
|
|
53
|
+
"node-opcua-service-query": "2.167.0",
|
|
54
|
+
"node-opcua-service-read": "2.167.0",
|
|
55
|
+
"node-opcua-service-register-node": "2.167.0",
|
|
56
|
+
"node-opcua-service-secure-channel": "2.167.0",
|
|
57
|
+
"node-opcua-service-session": "2.167.0",
|
|
58
|
+
"node-opcua-service-subscription": "2.167.0",
|
|
59
|
+
"node-opcua-service-translate-browse-path": "2.167.0",
|
|
60
|
+
"node-opcua-service-write": "2.167.0",
|
|
61
|
+
"node-opcua-status-code": "2.167.0",
|
|
62
|
+
"node-opcua-transport": "2.167.0",
|
|
63
|
+
"node-opcua-types": "2.167.0",
|
|
63
64
|
"node-opcua-utils": "2.165.0",
|
|
64
|
-
"node-opcua-variant": "2.
|
|
65
|
+
"node-opcua-variant": "2.167.0",
|
|
65
66
|
"thenify-ex": "4.4.0"
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
68
|
-
"node-opcua-data-access": "2.
|
|
69
|
+
"node-opcua-data-access": "2.167.0",
|
|
69
70
|
"node-opcua-leak-detector": "2.165.0",
|
|
70
71
|
"node-opcua-test-helpers": "2.157.0"
|
|
71
72
|
},
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
"internet of things"
|
|
85
86
|
],
|
|
86
87
|
"homepage": "http://node-opcua.github.io/",
|
|
87
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "5decfa86ee53a36ecd3bb454e7bf6e3dd27c7a4e",
|
|
88
89
|
"files": [
|
|
89
90
|
"dist",
|
|
90
91
|
"source"
|
package/source/base_server.ts
CHANGED
|
@@ -13,7 +13,7 @@ import chalk from "chalk";
|
|
|
13
13
|
import { assert } from "node-opcua-assert";
|
|
14
14
|
import { getDefaultCertificateManager, makeSubject, type OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
15
15
|
import { performCertificateSanityCheck } from "node-opcua-client";
|
|
16
|
-
import { type IOPCUASecureObjectOptions, makeApplicationUrn, OPCUASecureObject } from "node-opcua-common";
|
|
16
|
+
import { type IOPCUASecureObjectOptions, invalidateCachedSecrets, makeApplicationUrn, OPCUASecureObject } from "node-opcua-common";
|
|
17
17
|
import { exploreCertificate } from "node-opcua-crypto/web";
|
|
18
18
|
import { coerceLocalizedText } from "node-opcua-data-model";
|
|
19
19
|
import { installPeriodicClockAdjustment, uninstallPeriodicClockAdjustment } from "node-opcua-date-time";
|
|
@@ -339,9 +339,7 @@ export class OPCUABaseServer extends OPCUASecureObject {
|
|
|
339
339
|
// recreate with current hostnames
|
|
340
340
|
await this.createDefaultCertificate();
|
|
341
341
|
// invalidate cached cert so next getCertificate() reloads from disk
|
|
342
|
-
|
|
343
|
-
priv.$$certificate = null;
|
|
344
|
-
priv.$$certificateChain = null;
|
|
342
|
+
invalidateCachedSecrets(this);
|
|
345
343
|
}
|
|
346
344
|
|
|
347
345
|
private _checkCertificateSanMismatch(): void {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module node-opcua-server
|
|
3
|
+
*/
|
|
4
|
+
import type { Nonce } from "node-opcua-crypto/web";
|
|
5
|
+
|
|
6
|
+
export interface ExtractPasswordResult {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
password: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Extract the password from a decrypted UserNameIdentityToken
|
|
13
|
+
* password blob.
|
|
14
|
+
*
|
|
15
|
+
* The plaintext layout (OPC UA Part 4 §7.36.3) is:
|
|
16
|
+
*
|
|
17
|
+
* ┌─────────────────┬──────────────┬──────────────┐
|
|
18
|
+
* │ UInt32 length │ password │ serverNonce │
|
|
19
|
+
* │ (LE, 4 bytes) │ (variable) │ (32 bytes) │
|
|
20
|
+
* └─────────────────┴──────────────┴──────────────┘
|
|
21
|
+
*
|
|
22
|
+
* `length` = password.length + serverNonce.length
|
|
23
|
+
*
|
|
24
|
+
* After extracting the password, the trailing bytes **must**
|
|
25
|
+
* equal the session's `serverNonce` to confirm nonce binding.
|
|
26
|
+
*
|
|
27
|
+
* @param decryptedBuffer - the RSA-decrypted blob
|
|
28
|
+
* @param serverNonce - the nonce that was sent during
|
|
29
|
+
* CreateSession / last ActivateSession
|
|
30
|
+
*/
|
|
31
|
+
export function extractPasswordFromDecryptedBlob(
|
|
32
|
+
decryptedBuffer: Buffer,
|
|
33
|
+
serverNonce: Nonce
|
|
34
|
+
): ExtractPasswordResult {
|
|
35
|
+
const invalidResult: ExtractPasswordResult = {
|
|
36
|
+
valid: false,
|
|
37
|
+
password: ""
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// need at least 4 bytes for the length field
|
|
41
|
+
if (!decryptedBuffer || decryptedBuffer.length < 4) {
|
|
42
|
+
return invalidResult;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const totalLength = decryptedBuffer.readUInt32LE(0);
|
|
46
|
+
const passwordLength = totalLength - serverNonce.length;
|
|
47
|
+
|
|
48
|
+
// bounds check: password length must be non-negative
|
|
49
|
+
// and the buffer must be large enough
|
|
50
|
+
if (passwordLength < 0) {
|
|
51
|
+
return invalidResult;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const expectedBufferSize = 4 + passwordLength + serverNonce.length;
|
|
55
|
+
if (decryptedBuffer.length < expectedBufferSize) {
|
|
56
|
+
return invalidResult;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const password = decryptedBuffer
|
|
60
|
+
.subarray(4, 4 + passwordLength)
|
|
61
|
+
.toString("utf-8");
|
|
62
|
+
|
|
63
|
+
// verify that the trailing bytes match the server nonce
|
|
64
|
+
// (nonce binding — ensures session integrity)
|
|
65
|
+
const trailingNonce = decryptedBuffer.subarray(
|
|
66
|
+
4 + passwordLength,
|
|
67
|
+
4 + passwordLength + serverNonce.length
|
|
68
|
+
);
|
|
69
|
+
if (!trailingNonce.equals(serverNonce)) {
|
|
70
|
+
return invalidResult;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { valid: true, password };
|
|
74
|
+
}
|
package/source/opcua_server.ts
CHANGED
|
@@ -37,7 +37,7 @@ import { assert } from "node-opcua-assert";
|
|
|
37
37
|
import type { ByteString, UAString } from "node-opcua-basic-types";
|
|
38
38
|
import { getDefaultCertificateManager, type OPCUACertificateManager } from "node-opcua-certificate-manager";
|
|
39
39
|
import { ServerState } from "node-opcua-common";
|
|
40
|
-
import { type Certificate, exploreCertificate, type Nonce } from "node-opcua-crypto/web";
|
|
40
|
+
import { type Certificate, combine_der, exploreCertificate, type Nonce } from "node-opcua-crypto/web";
|
|
41
41
|
import {
|
|
42
42
|
AttributeIds,
|
|
43
43
|
filterDiagnosticOperationLevel,
|
|
@@ -149,6 +149,7 @@ import { DataType, type Variant, VariantArrayType } from "node-opcua-variant";
|
|
|
149
149
|
import { withCallback } from "thenify-ex";
|
|
150
150
|
|
|
151
151
|
import { OPCUABaseServer, type OPCUABaseServerOptions } from "./base_server";
|
|
152
|
+
import { extractPasswordFromDecryptedBlob } from "./extract_password_from_blob";
|
|
152
153
|
import { Factory } from "./factory";
|
|
153
154
|
import type { IChannelData } from "./i_channel_data";
|
|
154
155
|
import type { IRegisterServerManager } from "./i_register_server_manager";
|
|
@@ -159,7 +160,14 @@ import { RegisterServerManagerHidden } from "./register_server_manager_hidden";
|
|
|
159
160
|
import { RegisterServerManagerMDNSONLY } from "./register_server_manager_mdns_only";
|
|
160
161
|
import type { SamplingFunc } from "./sampling_func";
|
|
161
162
|
import type { ServerCapabilitiesOptions } from "./server_capabilities";
|
|
162
|
-
import {
|
|
163
|
+
import {
|
|
164
|
+
type AdvertisedEndpoint,
|
|
165
|
+
type EndpointDescriptionEx,
|
|
166
|
+
type IServerTransportSettings,
|
|
167
|
+
normalizeAdvertisedEndpoints,
|
|
168
|
+
OPCUAServerEndPoint,
|
|
169
|
+
parseOpcTcpUrl
|
|
170
|
+
} from "./server_end_point";
|
|
163
171
|
import { type ClosingReason, type CreateSessionOption, ServerEngine } from "./server_engine";
|
|
164
172
|
import type { ServerSession } from "./server_session";
|
|
165
173
|
import type { CreateMonitoredItemHook, DeleteMonitoredItemHook, Subscription } from "./server_subscription";
|
|
@@ -1877,14 +1885,12 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
1877
1885
|
|
|
1878
1886
|
const buff = cryptoFactory.asymmetricDecrypt(password, serverPrivateKey);
|
|
1879
1887
|
|
|
1880
|
-
|
|
1881
|
-
if (!
|
|
1888
|
+
const result = extractPasswordFromDecryptedBlob(buff, serverNonce);
|
|
1889
|
+
if (!result.valid) {
|
|
1882
1890
|
setImmediate(() => callback(null, false));
|
|
1883
1891
|
return;
|
|
1884
1892
|
}
|
|
1885
|
-
|
|
1886
|
-
const length = buff.readUInt32LE(0) - serverNonce.length;
|
|
1887
|
-
password = buff.subarray(4, 4 + length).toString("utf-8");
|
|
1893
|
+
password = result.password;
|
|
1888
1894
|
}
|
|
1889
1895
|
|
|
1890
1896
|
this.userManager
|
|
@@ -2114,7 +2120,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
2114
2120
|
// If the securityPolicyUri is None and none of the UserTokenPolicies requires
|
|
2115
2121
|
// encryption, the Server shall not send an ApplicationInstanceCertificate and the Client
|
|
2116
2122
|
// shall ignore the ApplicationInstanceCertificate.
|
|
2117
|
-
serverCertificate: hasEncryption ? serverCertificateChain : undefined,
|
|
2123
|
+
serverCertificate: hasEncryption && serverCertificateChain.length > 0 ? combine_der(serverCertificateChain) : undefined,
|
|
2118
2124
|
|
|
2119
2125
|
// The endpoints provided by the server.
|
|
2120
2126
|
// The Server shall return a set of EndpointDescriptions available for the serverUri
|
|
@@ -3819,7 +3825,7 @@ export class OPCUAServer extends OPCUABaseServer {
|
|
|
3819
3825
|
// xx hostname,
|
|
3820
3826
|
resourcePath: serverOption.resourcePath || "",
|
|
3821
3827
|
|
|
3822
|
-
advertisedEndpoints: endpointOptions.advertisedEndpoints
|
|
3828
|
+
advertisedEndpoints: endpointOptions.advertisedEndpoints
|
|
3823
3829
|
|
|
3824
3830
|
// TODO userTokenTypes: endpointOptions.userTokenTypes || undefined,
|
|
3825
3831
|
|
|
@@ -4048,7 +4054,7 @@ export interface RaiseEventAuditActivateSessionEventData extends RaiseEventAudit
|
|
|
4048
4054
|
}
|
|
4049
4055
|
|
|
4050
4056
|
// tslint:disable:no-empty-interface
|
|
4051
|
-
export interface RaiseEventTransitionEventData extends RaiseEventData {
|
|
4057
|
+
export interface RaiseEventTransitionEventData extends RaiseEventData {}
|
|
4052
4058
|
|
|
4053
4059
|
export interface RaiseEventAuditUrlMismatchEventTypeData extends RaiseEventData {
|
|
4054
4060
|
endpointUrl: PseudoVariantString;
|
|
@@ -4078,7 +4084,7 @@ export interface RaiseAuditCertificateDataMismatchEventData extends RaiseAuditCe
|
|
|
4078
4084
|
*/
|
|
4079
4085
|
invalidUri: PseudoVariantString;
|
|
4080
4086
|
}
|
|
4081
|
-
export interface RaiseAuditCertificateUntrustedEventData extends RaiseAuditCertificateEventData {
|
|
4087
|
+
export interface RaiseAuditCertificateUntrustedEventData extends RaiseAuditCertificateEventData {}
|
|
4082
4088
|
/**
|
|
4083
4089
|
* This EventType inherits all Properties of the AuditCertificateEventType.
|
|
4084
4090
|
*
|
|
@@ -4090,7 +4096,7 @@ export interface RaiseAuditCertificateUntrustedEventData extends RaiseAuditCerti
|
|
|
4090
4096
|
* There are no additional Properties defined for this EventType.
|
|
4091
4097
|
*
|
|
4092
4098
|
*/
|
|
4093
|
-
export interface RaiseAuditCertificateExpiredEventData extends RaiseAuditCertificateEventData {
|
|
4099
|
+
export interface RaiseAuditCertificateExpiredEventData extends RaiseAuditCertificateEventData {}
|
|
4094
4100
|
/**
|
|
4095
4101
|
* This EventType inherits all Properties of the AuditCertificateEventType.
|
|
4096
4102
|
*
|
|
@@ -4100,7 +4106,7 @@ export interface RaiseAuditCertificateExpiredEventData extends RaiseAuditCertifi
|
|
|
4100
4106
|
*
|
|
4101
4107
|
* There are no additional Properties defined for this EventType.
|
|
4102
4108
|
*/
|
|
4103
|
-
export interface RaiseAuditCertificateInvalidEventData extends RaiseAuditCertificateEventData {
|
|
4109
|
+
export interface RaiseAuditCertificateInvalidEventData extends RaiseAuditCertificateEventData {}
|
|
4104
4110
|
/**
|
|
4105
4111
|
* This EventType inherits all Properties of the AuditCertificateEventType.
|
|
4106
4112
|
*
|
|
@@ -4110,7 +4116,7 @@ export interface RaiseAuditCertificateInvalidEventData extends RaiseAuditCertifi
|
|
|
4110
4116
|
* If a trust chain is involved then the certificate that failed in the trust chain should be described.
|
|
4111
4117
|
* There are no additional Properties defined for this EventType.
|
|
4112
4118
|
*/
|
|
4113
|
-
export interface RaiseAuditCertificateUntrustedEventData extends RaiseAuditCertificateEventData {
|
|
4119
|
+
export interface RaiseAuditCertificateUntrustedEventData extends RaiseAuditCertificateEventData {}
|
|
4114
4120
|
/**
|
|
4115
4121
|
* This EventType inherits all Properties of the AuditCertificateEventType.
|
|
4116
4122
|
*
|
|
@@ -4133,7 +4139,7 @@ export interface RaiseAuditCertificateRevokedEventData extends RaiseAuditCertifi
|
|
|
4133
4139
|
*
|
|
4134
4140
|
* There are no additional Properties defined for this EventType
|
|
4135
4141
|
*/
|
|
4136
|
-
export interface RaiseAuditCertificateMismatchEventData extends RaiseAuditCertificateEventData {
|
|
4142
|
+
export interface RaiseAuditCertificateMismatchEventData extends RaiseAuditCertificateEventData {}
|
|
4137
4143
|
|
|
4138
4144
|
const opts = { multiArgs: false };
|
|
4139
4145
|
OPCUAServer.prototype.initialize = withCallback(OPCUAServer.prototype.initialize, opts);
|