node-opcua-chunkmanager
Advanced tools
Comparing version 2.128.0 to 2.129.0
@@ -0,1 +1,3 @@ | ||
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
/*** | ||
@@ -6,6 +8,6 @@ * @module node-opcua-chunkmanager | ||
export declare function verify_message_chunk(messageChunk: Buffer): void; | ||
export type WriteHeaderFunc = (chunk: Buffer, isLast: boolean, expectedLength: number) => void; | ||
export type WriteSequenceHeaderFunc = (chunk: Buffer) => void; | ||
export type SignBufferFunc = (buffer: Buffer) => Buffer; | ||
export type EncryptBufferFunc = (buffer: Buffer) => Buffer; | ||
export type WriteHeaderFunc = (this: ChunkManager, chunk: Buffer, isLast: boolean, expectedLength: number) => void; | ||
export type WriteSequenceHeaderFunc = (this: ChunkManager, chunk: Buffer) => void; | ||
export type SignBufferFunc = (this: ChunkManager, buffer: Buffer) => Buffer; | ||
export type EncryptBufferFunc = (this: ChunkManager, buffer: Buffer) => Buffer; | ||
export interface IChunkManagerOptions { | ||
@@ -23,21 +25,18 @@ chunkSize: number; | ||
} | ||
export declare enum Mode { | ||
None = 1, | ||
Sign = 2, | ||
SignAndEncrypt = 3 | ||
} | ||
export declare class ChunkManager extends EventEmitter { | ||
signBufferFunc?: SignBufferFunc; | ||
encryptBufferFunc?: EncryptBufferFunc; | ||
writeSequenceHeaderFunc?: WriteSequenceHeaderFunc; | ||
writeHeaderFunc?: WriteHeaderFunc; | ||
private readonly chunkSize; | ||
private readonly headerSize; | ||
private readonly signatureLength; | ||
private readonly sequenceHeaderSize; | ||
private readonly cipherBlockSize; | ||
private readonly plainBlockSize; | ||
private readonly maxBodySize; | ||
private readonly maxBlock?; | ||
private readonly dataOffset; | ||
private chunk; | ||
private cursor; | ||
private pendingChunk; | ||
private dataEnd; | ||
constructor(options: IChunkManagerOptions); | ||
#private; | ||
readonly chunkSize: number; | ||
readonly headerSize: number; | ||
readonly maxBodySize: number; | ||
readonly signatureLength: number; | ||
readonly sequenceHeaderSize: number; | ||
readonly cipherBlockSize: number; | ||
readonly plainBlockSize: number; | ||
readonly securityMode: Mode; | ||
constructor(securityMode: Mode, options: IChunkManagerOptions); | ||
evaluateTotalLengthAndChunks(bodySize: number): { | ||
@@ -49,14 +48,2 @@ totalLength: number; | ||
end(): void; | ||
/** | ||
* compute the signature of the chunk and append it at the end | ||
* of the data block. | ||
* | ||
* @method _write_signature | ||
* @private | ||
*/ | ||
private _write_signature; | ||
private _encrypt; | ||
private _push_pending_chunk; | ||
private _write_padding_bytes; | ||
private _post_process_current_chunk; | ||
} |
"use strict"; | ||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
if (kind === "m") throw new TypeError("Private method is not writable"); | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); | ||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; | ||
}; | ||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
}; | ||
var _ChunkManager_instances, _ChunkManager_signBufferFunc, _ChunkManager_encryptBufferFunc, _ChunkManager_writeSequenceHeaderFunc, _ChunkManager_writeHeaderFunc, _ChunkManager_maxBlock, _ChunkManager_dataOffset, _ChunkManager_chunk, _ChunkManager_cursor, _ChunkManager_pendingChunk, _ChunkManager_dataEnd, _ChunkManager__write_signature, _ChunkManager__encrypt, _ChunkManager__push_pending_chunk, _ChunkManager__write_padding_bytes, _ChunkManager__post_process_current_chunk; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ChunkManager = void 0; | ||
exports.verify_message_chunk = verify_message_chunk; | ||
exports.ChunkManager = exports.Mode = exports.verify_message_chunk = void 0; | ||
/*** | ||
@@ -21,5 +32,25 @@ * @module node-opcua-chunkmanager | ||
} | ||
exports.verify_message_chunk = verify_message_chunk; | ||
var Mode; | ||
(function (Mode) { | ||
Mode[Mode["None"] = 1] = "None"; | ||
Mode[Mode["Sign"] = 2] = "Sign"; | ||
Mode[Mode["SignAndEncrypt"] = 3] = "SignAndEncrypt"; | ||
})(Mode || (exports.Mode = Mode = {})); | ||
class ChunkManager extends events_1.EventEmitter { | ||
constructor(options) { | ||
constructor(securityMode, options) { | ||
super(); | ||
_ChunkManager_instances.add(this); | ||
_ChunkManager_signBufferFunc.set(this, void 0); | ||
_ChunkManager_encryptBufferFunc.set(this, void 0); | ||
_ChunkManager_writeSequenceHeaderFunc.set(this, void 0); | ||
_ChunkManager_writeHeaderFunc.set(this, void 0); | ||
// -------------- | ||
_ChunkManager_maxBlock.set(this, void 0); | ||
_ChunkManager_dataOffset.set(this, void 0); | ||
_ChunkManager_chunk.set(this, void 0); | ||
_ChunkManager_cursor.set(this, void 0); | ||
_ChunkManager_pendingChunk.set(this, void 0); | ||
_ChunkManager_dataEnd.set(this, void 0); | ||
this.securityMode = securityMode; | ||
// { chunkSize : 32, headerSize : 10 ,signatureLength: 32 } | ||
@@ -29,22 +60,25 @@ this.chunkSize = options.chunkSize; | ||
if (this.headerSize) { | ||
this.writeHeaderFunc = options.writeHeaderFunc; | ||
(0, node_opcua_assert_1.assert)(typeof this.writeHeaderFunc === "function"); | ||
__classPrivateFieldSet(this, _ChunkManager_writeHeaderFunc, options.writeHeaderFunc, "f"); | ||
(0, node_opcua_assert_1.assert)(typeof __classPrivateFieldGet(this, _ChunkManager_writeHeaderFunc, "f") === "function"); | ||
} | ||
this.sequenceHeaderSize = options.sequenceHeaderSize === undefined ? 8 : options.sequenceHeaderSize; | ||
if (this.sequenceHeaderSize > 0) { | ||
this.writeSequenceHeaderFunc = options.writeSequenceHeaderFunc; | ||
(0, node_opcua_assert_1.assert)(typeof this.writeSequenceHeaderFunc === "function"); | ||
__classPrivateFieldSet(this, _ChunkManager_writeSequenceHeaderFunc, options.writeSequenceHeaderFunc, "f"); | ||
(0, node_opcua_assert_1.assert)(typeof __classPrivateFieldGet(this, _ChunkManager_writeSequenceHeaderFunc, "f") === "function"); | ||
} | ||
this.signatureLength = options.signatureLength || 0; | ||
this.signBufferFunc = options.signBufferFunc; | ||
__classPrivateFieldSet(this, _ChunkManager_signBufferFunc, options.signBufferFunc, "f"); | ||
this.plainBlockSize = options.plainBlockSize || 0; // 256-14; | ||
this.cipherBlockSize = options.cipherBlockSize || 0; // 256; | ||
this.dataEnd = 0; | ||
__classPrivateFieldSet(this, _ChunkManager_dataEnd, 0, "f"); | ||
if (this.cipherBlockSize === 0) { | ||
(0, node_opcua_assert_1.assert)(securityMode === Mode.None || securityMode === Mode.Sign); | ||
// we don't encrypt,we just sign | ||
(0, node_opcua_assert_1.assert)(this.plainBlockSize === 0); | ||
// unencrypted block | ||
this.maxBodySize = this.chunkSize - this.headerSize - this.signatureLength - this.sequenceHeaderSize; | ||
this.encryptBufferFunc = undefined; | ||
__classPrivateFieldSet(this, _ChunkManager_encryptBufferFunc, undefined, "f"); | ||
} | ||
else { | ||
(0, node_opcua_assert_1.assert)(securityMode === Mode.SignAndEncrypt || securityMode === Mode.Sign); | ||
(0, node_opcua_assert_1.assert)(this.plainBlockSize !== 0); | ||
@@ -54,4 +88,4 @@ // During encryption a block with a size equal to PlainTextBlockSize is processed to produce a block | ||
// be the same. | ||
this.encryptBufferFunc = options.encryptBufferFunc; | ||
(0, node_opcua_assert_1.assert)(typeof this.encryptBufferFunc === "function", "an encryptBufferFunc is required"); | ||
__classPrivateFieldSet(this, _ChunkManager_encryptBufferFunc, options.encryptBufferFunc, "f"); | ||
(0, node_opcua_assert_1.assert)(typeof __classPrivateFieldGet(this, _ChunkManager_encryptBufferFunc, "f") === "function", "an encryptBufferFunc is required"); | ||
// this is the formula proposed by OPCUA | ||
@@ -63,4 +97,4 @@ this.maxBodySize = | ||
// this is the formula proposed by ERN | ||
this.maxBlock = Math.floor((this.chunkSize - this.headerSize) / this.cipherBlockSize); | ||
this.maxBodySize = this.plainBlockSize * this.maxBlock - this.sequenceHeaderSize - this.signatureLength - 1; | ||
__classPrivateFieldSet(this, _ChunkManager_maxBlock, Math.floor((this.chunkSize - this.headerSize) / this.cipherBlockSize), "f"); | ||
this.maxBodySize = this.plainBlockSize * __classPrivateFieldGet(this, _ChunkManager_maxBlock, "f") - this.sequenceHeaderSize - this.signatureLength - 1; | ||
if (this.plainBlockSize > 256) { | ||
@@ -72,6 +106,6 @@ this.maxBodySize -= 1; | ||
// where the data starts in the block | ||
this.dataOffset = this.headerSize + this.sequenceHeaderSize; | ||
this.chunk = null; | ||
this.cursor = 0; | ||
this.pendingChunk = null; | ||
__classPrivateFieldSet(this, _ChunkManager_dataOffset, this.headerSize + this.sequenceHeaderSize, "f"); | ||
__classPrivateFieldSet(this, _ChunkManager_chunk, null, "f"); | ||
__classPrivateFieldSet(this, _ChunkManager_cursor, 0, "f"); | ||
__classPrivateFieldSet(this, _ChunkManager_pendingChunk, null, "f"); | ||
} | ||
@@ -91,16 +125,16 @@ evaluateTotalLengthAndChunks(bodySize) { | ||
(0, node_opcua_assert_1.assert)(length - inputCursor !== 0); | ||
if (this.cursor === 0) { | ||
this._push_pending_chunk(false); | ||
if (__classPrivateFieldGet(this, _ChunkManager_cursor, "f") === 0) { | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__push_pending_chunk).call(this, false); | ||
} | ||
// space left in current chunk | ||
const spaceLeft = this.maxBodySize - this.cursor; | ||
const spaceLeft = this.maxBodySize - __classPrivateFieldGet(this, _ChunkManager_cursor, "f"); | ||
const nbToWrite = Math.min(length - inputCursor, spaceLeft); | ||
this.chunk = this.chunk || (0, node_opcua_buffer_utils_1.createFastUninitializedBuffer)(this.chunkSize); | ||
__classPrivateFieldSet(this, _ChunkManager_chunk, __classPrivateFieldGet(this, _ChunkManager_chunk, "f") || (0, node_opcua_buffer_utils_1.createFastUninitializedBuffer)(this.chunkSize), "f"); | ||
if (buffer) { | ||
buffer.copy(this.chunk, this.cursor + this.dataOffset, inputCursor, inputCursor + nbToWrite); | ||
buffer.copy(__classPrivateFieldGet(this, _ChunkManager_chunk, "f"), __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + __classPrivateFieldGet(this, _ChunkManager_dataOffset, "f"), inputCursor, inputCursor + nbToWrite); | ||
} | ||
inputCursor += nbToWrite; | ||
this.cursor += nbToWrite; | ||
if (this.cursor >= this.maxBodySize) { | ||
this._post_process_current_chunk(); | ||
__classPrivateFieldSet(this, _ChunkManager_cursor, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + nbToWrite, "f"); | ||
if (__classPrivateFieldGet(this, _ChunkManager_cursor, "f") >= this.maxBodySize) { | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__post_process_current_chunk).call(this); | ||
} | ||
@@ -111,119 +145,116 @@ l -= nbToWrite; | ||
end() { | ||
if (this.cursor > 0) { | ||
this._post_process_current_chunk(); | ||
if (__classPrivateFieldGet(this, _ChunkManager_cursor, "f") > 0) { | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__post_process_current_chunk).call(this); | ||
} | ||
this._push_pending_chunk(true); | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__push_pending_chunk).call(this, true); | ||
} | ||
/** | ||
* compute the signature of the chunk and append it at the end | ||
* of the data block. | ||
* | ||
* @method _write_signature | ||
* @private | ||
*/ | ||
_write_signature(chunk) { | ||
if (this.signBufferFunc) { | ||
(0, node_opcua_assert_1.assert)(typeof this.signBufferFunc === "function"); | ||
(0, node_opcua_assert_1.assert)(this.signatureLength !== 0); | ||
const signatureStart = this.dataEnd; | ||
const sectionToSign = chunk.subarray(0, signatureStart); | ||
const signature = this.signBufferFunc(sectionToSign); | ||
(0, node_opcua_assert_1.assert)(signature.length === this.signatureLength, "expecting signature length to match"); | ||
signature.copy(chunk, signatureStart); | ||
} | ||
exports.ChunkManager = ChunkManager; | ||
_ChunkManager_signBufferFunc = new WeakMap(), _ChunkManager_encryptBufferFunc = new WeakMap(), _ChunkManager_writeSequenceHeaderFunc = new WeakMap(), _ChunkManager_writeHeaderFunc = new WeakMap(), _ChunkManager_maxBlock = new WeakMap(), _ChunkManager_dataOffset = new WeakMap(), _ChunkManager_chunk = new WeakMap(), _ChunkManager_cursor = new WeakMap(), _ChunkManager_pendingChunk = new WeakMap(), _ChunkManager_dataEnd = new WeakMap(), _ChunkManager_instances = new WeakSet(), _ChunkManager__write_signature = function _ChunkManager__write_signature(chunk) { | ||
if (this.securityMode === Mode.None) { | ||
(0, node_opcua_assert_1.assert)(this.signatureLength === 0, "expecting NO SIGN"); | ||
return; | ||
} | ||
if (__classPrivateFieldGet(this, _ChunkManager_signBufferFunc, "f")) { | ||
(0, node_opcua_assert_1.assert)(typeof __classPrivateFieldGet(this, _ChunkManager_signBufferFunc, "f") === "function"); | ||
(0, node_opcua_assert_1.assert)(this.signatureLength !== 0); | ||
const signatureStart = __classPrivateFieldGet(this, _ChunkManager_dataEnd, "f"); | ||
const sectionToSign = chunk.subarray(0, signatureStart); | ||
const signature = __classPrivateFieldGet(this, _ChunkManager_signBufferFunc, "f").call(this, sectionToSign); | ||
(0, node_opcua_assert_1.assert)(signature.length === this.signatureLength, "expecting signature length to match"); | ||
signature.copy(chunk, signatureStart); | ||
} | ||
else { | ||
(0, node_opcua_assert_1.assert)(this.signatureLength === 0, "expecting NO SIGN"); | ||
} | ||
}, _ChunkManager__encrypt = function _ChunkManager__encrypt(chunk) { | ||
if (this.securityMode === Mode.None) { | ||
// nothing todo | ||
return; | ||
} | ||
if (this.plainBlockSize > 0 && __classPrivateFieldGet(this, _ChunkManager_encryptBufferFunc, "f")) { | ||
(0, node_opcua_assert_1.assert)(__classPrivateFieldGet(this, _ChunkManager_dataEnd, "f") !== undefined); | ||
const startEncryptionPos = this.headerSize; | ||
const endEncryptionPos = __classPrivateFieldGet(this, _ChunkManager_dataEnd, "f") + this.signatureLength; | ||
const areaToEncrypt = chunk.subarray(startEncryptionPos, endEncryptionPos); | ||
(0, node_opcua_assert_1.assert)(areaToEncrypt.length % this.plainBlockSize === 0); // padding should have been applied | ||
const nbBlock = areaToEncrypt.length / this.plainBlockSize; | ||
const encryptedBuffer = __classPrivateFieldGet(this, _ChunkManager_encryptBufferFunc, "f").call(this, areaToEncrypt); | ||
(0, node_opcua_assert_1.assert)(encryptedBuffer.length % this.cipherBlockSize === 0); | ||
(0, node_opcua_assert_1.assert)(encryptedBuffer.length === nbBlock * this.cipherBlockSize); | ||
encryptedBuffer.copy(chunk, this.headerSize, 0); | ||
} | ||
}, _ChunkManager__push_pending_chunk = function _ChunkManager__push_pending_chunk(isLast) { | ||
if (__classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f")) { | ||
const expectedLength = __classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f").length; | ||
if (this.headerSize > 0) { | ||
// Release 1.02 39 OPC Unified Architecture, Part 6: | ||
// The sequence header ensures that the first encrypted block of every Message sent over | ||
// a channel will start with different data. | ||
__classPrivateFieldGet(this, _ChunkManager_writeHeaderFunc, "f").call(this, __classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f").subarray(0, this.headerSize), isLast, expectedLength); | ||
} | ||
else { | ||
(0, node_opcua_assert_1.assert)(this.signatureLength === 0, "expecting NO SIGN"); | ||
if (this.sequenceHeaderSize > 0) { | ||
__classPrivateFieldGet(this, _ChunkManager_writeSequenceHeaderFunc, "f").call(this, __classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f").subarray(this.headerSize, this.headerSize + this.sequenceHeaderSize)); | ||
} | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__write_signature).call(this, __classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f")); | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__encrypt).call(this, __classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f")); | ||
/** | ||
* @event chunk | ||
* @param chunk {Buffer} | ||
* @param isLast {Boolean} , true if final chunk | ||
*/ | ||
this.emit("chunk", __classPrivateFieldGet(this, _ChunkManager_pendingChunk, "f"), isLast); | ||
__classPrivateFieldSet(this, _ChunkManager_pendingChunk, null, "f"); | ||
} | ||
_encrypt(chunk) { | ||
if (this.plainBlockSize > 0) { | ||
(0, node_opcua_assert_1.assert)(this.dataEnd !== undefined); | ||
const startEncryptionPos = this.headerSize; | ||
const endEncryptionPos = this.dataEnd + this.signatureLength; | ||
const areaToEncrypt = chunk.subarray(startEncryptionPos, endEncryptionPos); | ||
(0, node_opcua_assert_1.assert)(areaToEncrypt.length % this.plainBlockSize === 0); // padding should have been applied | ||
const nbBlock = areaToEncrypt.length / this.plainBlockSize; | ||
const encryptedBuffer = this.encryptBufferFunc(areaToEncrypt); | ||
(0, node_opcua_assert_1.assert)(encryptedBuffer.length % this.cipherBlockSize === 0); | ||
(0, node_opcua_assert_1.assert)(encryptedBuffer.length === nbBlock * this.cipherBlockSize); | ||
encryptedBuffer.copy(chunk, this.headerSize, 0); | ||
} | ||
}, _ChunkManager__write_padding_bytes = function _ChunkManager__write_padding_bytes(nbPaddingByteTotal) { | ||
const nbPaddingByte = nbPaddingByteTotal % 256; | ||
const extraNbPaddingByte = Math.floor(nbPaddingByteTotal / 256); | ||
(0, node_opcua_assert_1.assert)(extraNbPaddingByte === 0 || this.plainBlockSize > 256, "extraNbPaddingByte only requested when key size > 2048"); | ||
// write the padding byte | ||
__classPrivateFieldGet(this, _ChunkManager_chunk, "f").writeUInt8(nbPaddingByte, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + __classPrivateFieldGet(this, _ChunkManager_dataOffset, "f")); | ||
__classPrivateFieldSet(this, _ChunkManager_cursor, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + 1, "f"); | ||
for (let i = 0; i < nbPaddingByteTotal; i++) { | ||
__classPrivateFieldGet(this, _ChunkManager_chunk, "f").writeUInt8(nbPaddingByte, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + __classPrivateFieldGet(this, _ChunkManager_dataOffset, "f") + i); | ||
} | ||
_push_pending_chunk(isLast) { | ||
if (this.pendingChunk) { | ||
const expectedLength = this.pendingChunk.length; | ||
if (this.headerSize > 0) { | ||
// Release 1.02 39 OPC Unified Architecture, Part 6: | ||
// The sequence header ensures that the first encrypted block of every Message sent over | ||
// a channel will start with different data. | ||
this.writeHeaderFunc(this.pendingChunk.subarray(0, this.headerSize), isLast, expectedLength); | ||
} | ||
if (this.sequenceHeaderSize > 0) { | ||
this.writeSequenceHeaderFunc(this.pendingChunk.subarray(this.headerSize, this.headerSize + this.sequenceHeaderSize)); | ||
} | ||
this._write_signature(this.pendingChunk); | ||
this._encrypt(this.pendingChunk); | ||
/** | ||
* @event chunk | ||
* @param chunk {Buffer} | ||
* @param isLast {Boolean} , true if final chunk | ||
*/ | ||
this.emit("chunk", this.pendingChunk, isLast); | ||
this.pendingChunk = null; | ||
} | ||
__classPrivateFieldSet(this, _ChunkManager_cursor, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + nbPaddingByteTotal, "f"); | ||
if (this.plainBlockSize > 256) { | ||
__classPrivateFieldGet(this, _ChunkManager_chunk, "f").writeUInt8(extraNbPaddingByte, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + __classPrivateFieldGet(this, _ChunkManager_dataOffset, "f")); | ||
__classPrivateFieldSet(this, _ChunkManager_cursor, __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + 1, "f"); | ||
} | ||
_write_padding_bytes(nbPaddingByteTotal) { | ||
const nbPaddingByte = nbPaddingByteTotal % 256; | ||
const extraNbPaddingByte = Math.floor(nbPaddingByteTotal / 256); | ||
(0, node_opcua_assert_1.assert)(extraNbPaddingByte === 0 || this.plainBlockSize > 256, "extraNbPaddingByte only requested when key size > 2048"); | ||
// write the padding byte | ||
this.chunk.writeUInt8(nbPaddingByte, this.cursor + this.dataOffset); | ||
this.cursor += 1; | ||
for (let i = 0; i < nbPaddingByteTotal; i++) { | ||
this.chunk.writeUInt8(nbPaddingByte, this.cursor + this.dataOffset + i); | ||
} | ||
this.cursor += nbPaddingByteTotal; | ||
}, _ChunkManager__post_process_current_chunk = function _ChunkManager__post_process_current_chunk() { | ||
let extraEncryptionBytes = 0; | ||
// add padding bytes if needed | ||
if (this.plainBlockSize > 0) { | ||
// write padding ( if encryption ) | ||
// let's calculate curLength = the length of the block to encrypt without padding yet | ||
// +---------------+---------------+-------------+---------+--------------+------------+ | ||
// |SequenceHeader | data | paddingByte | padding | extraPadding | signature | | ||
// +---------------+---------------+-------------+---------+--------------+------------+ | ||
let curLength = this.sequenceHeaderSize + __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + this.signatureLength; | ||
if (this.plainBlockSize > 256) { | ||
this.chunk.writeUInt8(extraNbPaddingByte, this.cursor + this.dataOffset); | ||
this.cursor += 1; | ||
curLength += 2; // account for extraPadding Byte Number; | ||
} | ||
} | ||
_post_process_current_chunk() { | ||
let extraEncryptionBytes = 0; | ||
// add padding bytes if needed | ||
if (this.plainBlockSize > 0) { | ||
// write padding ( if encryption ) | ||
// let's calculate curLength = the length of the block to encrypt without padding yet | ||
// +---------------+---------------+-------------+---------+--------------+------------+ | ||
// |SequenceHeader | data | paddingByte | padding | extraPadding | signature | | ||
// +---------------+---------------+-------------+---------+--------------+------------+ | ||
let curLength = this.sequenceHeaderSize + this.cursor + this.signatureLength; | ||
if (this.plainBlockSize > 256) { | ||
curLength += 2; // account for extraPadding Byte Number; | ||
} | ||
else { | ||
curLength += 1; | ||
} | ||
// let's calculate the required number of padding bytes | ||
const n = curLength % this.plainBlockSize; | ||
const nbPaddingByteTotal = (this.plainBlockSize - n) % this.plainBlockSize; | ||
this._write_padding_bytes(nbPaddingByteTotal); | ||
const adjustedLength = this.sequenceHeaderSize + this.cursor + this.signatureLength; | ||
(0, node_opcua_assert_1.assert)(adjustedLength % this.plainBlockSize === 0); | ||
const nbBlock = adjustedLength / this.plainBlockSize; | ||
extraEncryptionBytes = nbBlock * (this.cipherBlockSize - this.plainBlockSize); | ||
else { | ||
curLength += 1; | ||
} | ||
this.dataEnd = this.dataOffset + this.cursor; | ||
// calculate the expected length of the chunk, once encrypted if encryption apply | ||
const expectedLength = this.dataEnd + this.signatureLength + extraEncryptionBytes; | ||
this.pendingChunk = this.chunk.subarray(0, expectedLength); | ||
// note : | ||
// - this.pending_chunk has the correct size but is not signed nor encrypted yet | ||
// as we don't know what to write in the header yet | ||
// - as a result, | ||
this.chunk = null; | ||
this.cursor = 0; | ||
// let's calculate the required number of padding bytes | ||
const n = curLength % this.plainBlockSize; | ||
const nbPaddingByteTotal = (this.plainBlockSize - n) % this.plainBlockSize; | ||
__classPrivateFieldGet(this, _ChunkManager_instances, "m", _ChunkManager__write_padding_bytes).call(this, nbPaddingByteTotal); | ||
const adjustedLength = this.sequenceHeaderSize + __classPrivateFieldGet(this, _ChunkManager_cursor, "f") + this.signatureLength; | ||
(0, node_opcua_assert_1.assert)(adjustedLength % this.plainBlockSize === 0); | ||
const nbBlock = adjustedLength / this.plainBlockSize; | ||
extraEncryptionBytes = nbBlock * (this.cipherBlockSize - this.plainBlockSize); | ||
} | ||
} | ||
exports.ChunkManager = ChunkManager; | ||
__classPrivateFieldSet(this, _ChunkManager_dataEnd, __classPrivateFieldGet(this, _ChunkManager_dataOffset, "f") + __classPrivateFieldGet(this, _ChunkManager_cursor, "f"), "f"); | ||
// calculate the expected length of the chunk, once encrypted if encryption apply | ||
const expectedLength = __classPrivateFieldGet(this, _ChunkManager_dataEnd, "f") + this.signatureLength + extraEncryptionBytes; | ||
__classPrivateFieldSet(this, _ChunkManager_pendingChunk, __classPrivateFieldGet(this, _ChunkManager_chunk, "f").subarray(0, expectedLength), "f"); | ||
// note : | ||
// - this.pending_chunk has the correct size but is not signed nor encrypted yet | ||
// as we don't know what to write in the header yet | ||
// - as a result, | ||
__classPrivateFieldSet(this, _ChunkManager_chunk, null, "f"); | ||
__classPrivateFieldSet(this, _ChunkManager_cursor, 0, "f"); | ||
}; | ||
//# sourceMappingURL=chunk_manager.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.readMessageHeader = readMessageHeader; | ||
exports.readMessageHeader = void 0; | ||
function readMessageHeader(stream) { | ||
const msgType = String.fromCharCode(stream.readUInt8()) + | ||
String.fromCharCode(stream.readUInt8()) + | ||
String.fromCharCode(stream.readUInt8()); | ||
const msgType = String.fromCharCode(stream.readUInt8()) + String.fromCharCode(stream.readUInt8()) + String.fromCharCode(stream.readUInt8()); | ||
const isFinal = String.fromCharCode(stream.readUInt8()); | ||
@@ -12,2 +10,3 @@ const length = stream.readUInt32(); | ||
} | ||
exports.readMessageHeader = readMessageHeader; | ||
//# sourceMappingURL=read_message_header.js.map |
{ | ||
"name": "node-opcua-chunkmanager", | ||
"version": "2.128.0", | ||
"version": "2.129.0", | ||
"description": "pure nodejs OPCUA SDK - module chunkmanager", | ||
@@ -39,3 +39,3 @@ "main": "./dist/index.js", | ||
"homepage": "http://node-opcua.github.io/", | ||
"gitHead": "67a73af6b831d8651a0e66ceb295f159cab4386b", | ||
"gitHead": "8a1754350fb95a764e278d37a289f0c48ccb8c9d", | ||
"files": [ | ||
@@ -42,0 +42,0 @@ "dist", |
@@ -52,6 +52,6 @@ /*** | ||
export type WriteHeaderFunc = (chunk: Buffer, isLast: boolean, expectedLength: number) => void; | ||
export type WriteSequenceHeaderFunc = (chunk: Buffer) => void; | ||
export type SignBufferFunc = (buffer: Buffer) => Buffer; | ||
export type EncryptBufferFunc = (buffer: Buffer) => Buffer; | ||
export type WriteHeaderFunc = (this: ChunkManager, chunk: Buffer, isLast: boolean, expectedLength: number) => void; | ||
export type WriteSequenceHeaderFunc = (this: ChunkManager, chunk: Buffer) => void; | ||
export type SignBufferFunc = (this: ChunkManager, buffer: Buffer) => Buffer; | ||
export type EncryptBufferFunc = (this: ChunkManager, buffer: Buffer) => Buffer; | ||
@@ -73,27 +73,38 @@ export interface IChunkManagerOptions { | ||
export enum Mode { | ||
None = 1, | ||
Sign = 2, | ||
SignAndEncrypt = 3 | ||
} | ||
export class ChunkManager extends EventEmitter { | ||
public signBufferFunc?: SignBufferFunc; | ||
public encryptBufferFunc?: EncryptBufferFunc; | ||
public writeSequenceHeaderFunc?: WriteSequenceHeaderFunc; | ||
public writeHeaderFunc?: WriteHeaderFunc; | ||
#signBufferFunc?: SignBufferFunc; | ||
#encryptBufferFunc?: EncryptBufferFunc; | ||
#writeSequenceHeaderFunc?: WriteSequenceHeaderFunc; | ||
#writeHeaderFunc?: WriteHeaderFunc; | ||
private readonly chunkSize: number; | ||
private readonly headerSize: number; | ||
private readonly signatureLength: number; | ||
private readonly sequenceHeaderSize: number; | ||
private readonly cipherBlockSize: number; | ||
private readonly plainBlockSize: number; | ||
public readonly chunkSize: number; | ||
public readonly headerSize: number; | ||
public readonly maxBodySize: number; | ||
public readonly signatureLength: number; | ||
public readonly sequenceHeaderSize: number; | ||
public readonly cipherBlockSize: number; | ||
public readonly plainBlockSize: number; | ||
// -------------- | ||
private readonly maxBodySize: number; | ||
private readonly maxBlock?: number; | ||
private readonly dataOffset: number; | ||
private chunk: Buffer | null; | ||
private cursor: number; | ||
private pendingChunk: Buffer | null; | ||
private dataEnd: number; | ||
readonly #maxBlock?: number; | ||
constructor(options: IChunkManagerOptions) { | ||
readonly #dataOffset: number; | ||
public readonly securityMode: Mode; | ||
#chunk: Buffer | null; | ||
#cursor: number; | ||
#pendingChunk: Buffer | null; | ||
#dataEnd: number; | ||
constructor(securityMode: Mode, options: IChunkManagerOptions) { | ||
super(); | ||
this.securityMode = securityMode; | ||
// { chunkSize : 32, headerSize : 10 ,signatureLength: 32 } | ||
@@ -104,4 +115,4 @@ this.chunkSize = options.chunkSize; | ||
if (this.headerSize) { | ||
this.writeHeaderFunc = options.writeHeaderFunc; | ||
assert(typeof this.writeHeaderFunc === "function"); | ||
this.#writeHeaderFunc = options.writeHeaderFunc; | ||
assert(typeof this.#writeHeaderFunc === "function"); | ||
} | ||
@@ -111,19 +122,22 @@ | ||
if (this.sequenceHeaderSize > 0) { | ||
this.writeSequenceHeaderFunc = options.writeSequenceHeaderFunc; | ||
assert(typeof this.writeSequenceHeaderFunc === "function"); | ||
this.#writeSequenceHeaderFunc = options.writeSequenceHeaderFunc; | ||
assert(typeof this.#writeSequenceHeaderFunc === "function"); | ||
} | ||
this.signatureLength = options.signatureLength || 0; | ||
this.signBufferFunc = options.signBufferFunc; | ||
this.#signBufferFunc = options.signBufferFunc; | ||
this.plainBlockSize = options.plainBlockSize || 0; // 256-14; | ||
this.cipherBlockSize = options.cipherBlockSize || 0; // 256; | ||
this.dataEnd = 0; | ||
this.#dataEnd = 0; | ||
if (this.cipherBlockSize === 0) { | ||
assert(securityMode === Mode.None || securityMode === Mode.Sign); | ||
// we don't encrypt,we just sign | ||
assert(this.plainBlockSize === 0); | ||
// unencrypted block | ||
this.maxBodySize = this.chunkSize - this.headerSize - this.signatureLength - this.sequenceHeaderSize; | ||
this.encryptBufferFunc = undefined; | ||
this.#encryptBufferFunc = undefined; | ||
} else { | ||
assert(securityMode === Mode.SignAndEncrypt || securityMode === Mode.Sign); | ||
assert(this.plainBlockSize !== 0); | ||
@@ -134,4 +148,4 @@ // During encryption a block with a size equal to PlainTextBlockSize is processed to produce a block | ||
this.encryptBufferFunc = options.encryptBufferFunc; | ||
assert(typeof this.encryptBufferFunc === "function", "an encryptBufferFunc is required"); | ||
this.#encryptBufferFunc = options.encryptBufferFunc; | ||
assert(typeof this.#encryptBufferFunc === "function", "an encryptBufferFunc is required"); | ||
@@ -145,4 +159,4 @@ // this is the formula proposed by OPCUA | ||
// this is the formula proposed by ERN | ||
this.maxBlock = Math.floor((this.chunkSize - this.headerSize) / this.cipherBlockSize); | ||
this.maxBodySize = this.plainBlockSize * this.maxBlock - this.sequenceHeaderSize - this.signatureLength - 1; | ||
this.#maxBlock = Math.floor((this.chunkSize - this.headerSize) / this.cipherBlockSize); | ||
this.maxBodySize = this.plainBlockSize * this.#maxBlock - this.sequenceHeaderSize - this.signatureLength - 1; | ||
@@ -156,7 +170,7 @@ if (this.plainBlockSize > 256) { | ||
// where the data starts in the block | ||
this.dataOffset = this.headerSize + this.sequenceHeaderSize; | ||
this.#dataOffset = this.headerSize + this.sequenceHeaderSize; | ||
this.chunk = null; | ||
this.cursor = 0; | ||
this.pendingChunk = null; | ||
this.#chunk = null; | ||
this.#cursor = 0; | ||
this.#pendingChunk = null; | ||
} | ||
@@ -181,22 +195,22 @@ | ||
if (this.cursor === 0) { | ||
this._push_pending_chunk(false); | ||
if (this.#cursor === 0) { | ||
this.#_push_pending_chunk(false); | ||
} | ||
// space left in current chunk | ||
const spaceLeft = this.maxBodySize - this.cursor; | ||
const spaceLeft = this.maxBodySize - this.#cursor; | ||
const nbToWrite = Math.min(length - inputCursor, spaceLeft); | ||
this.chunk = this.chunk || createFastUninitializedBuffer(this.chunkSize); | ||
this.#chunk = this.#chunk || createFastUninitializedBuffer(this.chunkSize); | ||
if (buffer) { | ||
buffer.copy(this.chunk!, this.cursor + this.dataOffset, inputCursor, inputCursor + nbToWrite); | ||
buffer.copy(this.#chunk!, this.#cursor + this.#dataOffset, inputCursor, inputCursor + nbToWrite); | ||
} | ||
inputCursor += nbToWrite; | ||
this.cursor += nbToWrite; | ||
this.#cursor += nbToWrite; | ||
if (this.cursor >= this.maxBodySize) { | ||
this._post_process_current_chunk(); | ||
if (this.#cursor >= this.maxBodySize) { | ||
this.#_post_process_current_chunk(); | ||
} | ||
@@ -208,6 +222,6 @@ l -= nbToWrite; | ||
public end() { | ||
if (this.cursor > 0) { | ||
this._post_process_current_chunk(); | ||
if (this.#cursor > 0) { | ||
this.#_post_process_current_chunk(); | ||
} | ||
this._push_pending_chunk(true); | ||
this.#_push_pending_chunk(true); | ||
} | ||
@@ -222,13 +236,16 @@ | ||
*/ | ||
private _write_signature(chunk: Buffer) { | ||
if (this.signBufferFunc) { | ||
assert(typeof this.signBufferFunc === "function"); | ||
#_write_signature(chunk: Buffer) { | ||
if (this.securityMode === Mode.None) { | ||
assert(this.signatureLength === 0, "expecting NO SIGN"); | ||
return; | ||
} | ||
if (this.#signBufferFunc) { | ||
assert(typeof this.#signBufferFunc === "function"); | ||
assert(this.signatureLength !== 0); | ||
const signatureStart = this.dataEnd; | ||
const signatureStart = this.#dataEnd; | ||
const sectionToSign = chunk.subarray(0, signatureStart); | ||
const signature = this.signBufferFunc(sectionToSign); | ||
assert(signature.length === this.signatureLength , "expecting signature length to match"); | ||
const signature = this.#signBufferFunc.call(this, sectionToSign); | ||
assert(signature.length === this.signatureLength, "expecting signature length to match"); | ||
signature.copy(chunk, signatureStart); | ||
@@ -240,7 +257,11 @@ } else { | ||
private _encrypt(chunk: Buffer) { | ||
if (this.plainBlockSize > 0) { | ||
assert(this.dataEnd !== undefined); | ||
#_encrypt(chunk: Buffer) { | ||
if (this.securityMode === Mode.None) { | ||
// nothing todo | ||
return; | ||
} | ||
if (this.plainBlockSize > 0 && this.#encryptBufferFunc) { | ||
assert(this.#dataEnd !== undefined); | ||
const startEncryptionPos = this.headerSize; | ||
const endEncryptionPos = this.dataEnd + this.signatureLength; | ||
const endEncryptionPos = this.#dataEnd + this.signatureLength; | ||
@@ -252,3 +273,3 @@ const areaToEncrypt = chunk.subarray(startEncryptionPos, endEncryptionPos); | ||
const encryptedBuffer = this.encryptBufferFunc!(areaToEncrypt); | ||
const encryptedBuffer = this.#encryptBufferFunc!.call(this, areaToEncrypt); | ||
assert(encryptedBuffer.length % this.cipherBlockSize === 0); | ||
@@ -261,5 +282,5 @@ assert(encryptedBuffer.length === nbBlock * this.cipherBlockSize); | ||
private _push_pending_chunk(isLast: boolean) { | ||
if (this.pendingChunk) { | ||
const expectedLength = this.pendingChunk.length; | ||
#_push_pending_chunk(isLast: boolean) { | ||
if (this.#pendingChunk) { | ||
const expectedLength = this.#pendingChunk.length; | ||
@@ -270,11 +291,14 @@ if (this.headerSize > 0) { | ||
// a channel will start with different data. | ||
this.writeHeaderFunc!(this.pendingChunk.subarray(0, this.headerSize), isLast, expectedLength); | ||
this.#writeHeaderFunc!.call(this, this.#pendingChunk.subarray(0, this.headerSize), isLast, expectedLength); | ||
} | ||
if (this.sequenceHeaderSize > 0) { | ||
this.writeSequenceHeaderFunc!(this.pendingChunk.subarray(this.headerSize, this.headerSize + this.sequenceHeaderSize)); | ||
this.#writeSequenceHeaderFunc!.call( | ||
this, | ||
this.#pendingChunk.subarray(this.headerSize, this.headerSize + this.sequenceHeaderSize) | ||
); | ||
} | ||
this._write_signature(this.pendingChunk); | ||
this.#_write_signature(this.#pendingChunk); | ||
this._encrypt(this.pendingChunk); | ||
this.#_encrypt(this.#pendingChunk); | ||
@@ -286,8 +310,8 @@ /** | ||
*/ | ||
this.emit("chunk", this.pendingChunk, isLast); | ||
this.pendingChunk = null; | ||
this.emit("chunk", this.#pendingChunk, isLast); | ||
this.#pendingChunk = null; | ||
} | ||
} | ||
private _write_padding_bytes(nbPaddingByteTotal: number) { | ||
#_write_padding_bytes(nbPaddingByteTotal: number) { | ||
const nbPaddingByte = nbPaddingByteTotal % 256; | ||
@@ -299,17 +323,17 @@ const extraNbPaddingByte = Math.floor(nbPaddingByteTotal / 256); | ||
// write the padding byte | ||
this.chunk!.writeUInt8(nbPaddingByte, this.cursor + this.dataOffset); | ||
this.cursor += 1; | ||
this.#chunk!.writeUInt8(nbPaddingByte, this.#cursor + this.#dataOffset); | ||
this.#cursor += 1; | ||
for (let i = 0; i < nbPaddingByteTotal; i++) { | ||
this.chunk!.writeUInt8(nbPaddingByte, this.cursor + this.dataOffset + i); | ||
this.#chunk!.writeUInt8(nbPaddingByte, this.#cursor + this.#dataOffset + i); | ||
} | ||
this.cursor += nbPaddingByteTotal; | ||
this.#cursor += nbPaddingByteTotal; | ||
if (this.plainBlockSize > 256) { | ||
this.chunk!.writeUInt8(extraNbPaddingByte, this.cursor + this.dataOffset); | ||
this.cursor += 1; | ||
this.#chunk!.writeUInt8(extraNbPaddingByte, this.#cursor + this.#dataOffset); | ||
this.#cursor += 1; | ||
} | ||
} | ||
private _post_process_current_chunk() { | ||
#_post_process_current_chunk() { | ||
let extraEncryptionBytes = 0; | ||
@@ -324,3 +348,3 @@ // add padding bytes if needed | ||
// +---------------+---------------+-------------+---------+--------------+------------+ | ||
let curLength = this.sequenceHeaderSize + this.cursor + this.signatureLength; | ||
let curLength = this.sequenceHeaderSize + this.#cursor + this.signatureLength; | ||
if (this.plainBlockSize > 256) { | ||
@@ -335,4 +359,4 @@ curLength += 2; // account for extraPadding Byte Number; | ||
this._write_padding_bytes(nbPaddingByteTotal); | ||
const adjustedLength = this.sequenceHeaderSize + this.cursor + this.signatureLength; | ||
this.#_write_padding_bytes(nbPaddingByteTotal); | ||
const adjustedLength = this.sequenceHeaderSize + this.#cursor + this.signatureLength; | ||
@@ -344,8 +368,8 @@ assert(adjustedLength % this.plainBlockSize === 0); | ||
this.dataEnd = this.dataOffset + this.cursor; | ||
this.#dataEnd = this.#dataOffset + this.#cursor; | ||
// calculate the expected length of the chunk, once encrypted if encryption apply | ||
const expectedLength = this.dataEnd + this.signatureLength + extraEncryptionBytes; | ||
const expectedLength = this.#dataEnd + this.signatureLength + extraEncryptionBytes; | ||
this.pendingChunk = this.chunk!.subarray(0, expectedLength); | ||
this.#pendingChunk = this.#chunk!.subarray(0, expectedLength); | ||
// note : | ||
@@ -355,5 +379,5 @@ // - this.pending_chunk has the correct size but is not signed nor encrypted yet | ||
// - as a result, | ||
this.chunk = null; | ||
this.cursor = 0; | ||
this.#chunk = null; | ||
this.#cursor = 0; | ||
} | ||
} |
@@ -8,10 +8,8 @@ /*** | ||
export function readMessageHeader(stream: BinaryStream): MessageHeader { | ||
const msgType = | ||
String.fromCharCode(stream.readUInt8()) + String.fromCharCode(stream.readUInt8()) + String.fromCharCode(stream.readUInt8()); | ||
const msgType = String.fromCharCode(stream.readUInt8()) + | ||
String.fromCharCode(stream.readUInt8()) + | ||
String.fromCharCode(stream.readUInt8()); | ||
const isFinal = String.fromCharCode(stream.readUInt8()); | ||
const length = stream.readUInt32(); | ||
return {msgType, isFinal, length}; | ||
return { msgType, isFinal, length }; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
55680
781
18