node-ikev2 0.1.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/LICENSE +201 -0
- package/README.md +86 -0
- package/lib/src/attribute.d.ts +67 -0
- package/lib/src/attribute.js +161 -0
- package/lib/src/header.d.ts +102 -0
- package/lib/src/header.js +307 -0
- package/lib/src/index.d.ts +9 -0
- package/lib/src/index.js +38 -0
- package/lib/src/message.d.ts +88 -0
- package/lib/src/message.js +264 -0
- package/lib/src/payload.d.ts +1013 -0
- package/lib/src/payload.js +1840 -0
- package/lib/src/proposal.d.ts +90 -0
- package/lib/src/proposal.js +200 -0
- package/lib/src/selector.d.ts +80 -0
- package/lib/src/selector.js +132 -0
- package/lib/src/transform.d.ts +316 -0
- package/lib/src/transform.js +470 -0
- package/package.json +56 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Message = void 0;
|
|
4
|
+
const header_1 = require("./header");
|
|
5
|
+
const payload_1 = require("./payload");
|
|
6
|
+
/**
|
|
7
|
+
* IKEv2 Message class
|
|
8
|
+
* @class
|
|
9
|
+
* @property {Header} header - IKEv2 header
|
|
10
|
+
* @property {Payload[]} payloads - IKEv2 payloads
|
|
11
|
+
*/
|
|
12
|
+
class Message {
|
|
13
|
+
/**
|
|
14
|
+
* @constructor
|
|
15
|
+
* @param {Header} header - IKEv2 header
|
|
16
|
+
* @param {Payload[]} payloads - IKEv2 payloads
|
|
17
|
+
*/
|
|
18
|
+
constructor(header, payloads) {
|
|
19
|
+
this.header = header;
|
|
20
|
+
this.payloads = payloads;
|
|
21
|
+
}
|
|
22
|
+
static getBuffer(packet) {
|
|
23
|
+
if (typeof packet === "string") {
|
|
24
|
+
if (packet.length === 0) {
|
|
25
|
+
throw new Error("Hex string cannot be empty");
|
|
26
|
+
}
|
|
27
|
+
if (!/^[0-9a-fA-F]+$/.test(packet)) {
|
|
28
|
+
throw new Error("Invalid hex string format");
|
|
29
|
+
}
|
|
30
|
+
return Buffer.from(packet, "hex");
|
|
31
|
+
}
|
|
32
|
+
else if (Buffer.isBuffer(packet)) {
|
|
33
|
+
return packet;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
throw new Error("Packet must be a Buffer or hex string");
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parses IKEv2 message
|
|
41
|
+
* @param buffer - Buffer containing the message
|
|
42
|
+
* @param headerOnly - If only header is needed (default: false)
|
|
43
|
+
* @returns {Message}
|
|
44
|
+
*/
|
|
45
|
+
static parse(packet, headerOnly = false) {
|
|
46
|
+
try {
|
|
47
|
+
// Input validation
|
|
48
|
+
if (!packet) {
|
|
49
|
+
throw new Error("Packet data is required");
|
|
50
|
+
}
|
|
51
|
+
let buffer = this.getBuffer(packet);
|
|
52
|
+
// Validate minimum packet size
|
|
53
|
+
if (buffer.length < header_1.Header.headerLength) {
|
|
54
|
+
throw new Error(`Packet too short. Expected at least ${header_1.Header.headerLength} bytes, got ${buffer.length}`);
|
|
55
|
+
}
|
|
56
|
+
const header = header_1.Header.parse(buffer);
|
|
57
|
+
// If only header is needed
|
|
58
|
+
if (headerOnly) {
|
|
59
|
+
return new Message(header, []);
|
|
60
|
+
}
|
|
61
|
+
// Validate that packet length matches header length
|
|
62
|
+
if (buffer.length < header.length) {
|
|
63
|
+
throw new Error(`Packet length mismatch. Header indicates ${header.length} bytes, but packet has ${buffer.length} bytes`);
|
|
64
|
+
}
|
|
65
|
+
let nextPayload = header.nextPayload;
|
|
66
|
+
let offset = header_1.Header.headerLength;
|
|
67
|
+
const payloads = [];
|
|
68
|
+
let nextPayloadClass = payload_1.payloadTypeMapping[nextPayload];
|
|
69
|
+
if (!nextPayloadClass) {
|
|
70
|
+
throw new Error(`Unknown payload type: ${nextPayload}`);
|
|
71
|
+
}
|
|
72
|
+
// Parse first payload
|
|
73
|
+
const firstPayload = nextPayloadClass.parse(buffer.subarray(header_1.Header.headerLength, buffer.length));
|
|
74
|
+
payloads.push(firstPayload);
|
|
75
|
+
offset += firstPayload.length;
|
|
76
|
+
// Validate payload length
|
|
77
|
+
if (offset > buffer.length) {
|
|
78
|
+
throw new Error(`Payload length exceeds packet size. Offset: ${offset}, Packet size: ${buffer.length}`);
|
|
79
|
+
}
|
|
80
|
+
if (!(firstPayload instanceof payload_1.PayloadSK)) {
|
|
81
|
+
nextPayload = firstPayload.nextPayload;
|
|
82
|
+
nextPayloadClass = payload_1.payloadTypeMapping[nextPayload];
|
|
83
|
+
// Parse subsequent payloads
|
|
84
|
+
while (offset < buffer.length &&
|
|
85
|
+
nextPayloadClass &&
|
|
86
|
+
nextPayload !== payload_1.payloadType.NONE) {
|
|
87
|
+
// Validate we have enough data for the next payload
|
|
88
|
+
if (offset + 4 > buffer.length) {
|
|
89
|
+
throw new Error(`Insufficient data for payload header at offset ${offset}`);
|
|
90
|
+
}
|
|
91
|
+
const payload = nextPayloadClass.parse(buffer.subarray(offset, buffer.length));
|
|
92
|
+
payloads.push(payload);
|
|
93
|
+
offset += payload.length;
|
|
94
|
+
nextPayload = payload.nextPayload;
|
|
95
|
+
nextPayloadClass = payload_1.payloadTypeMapping[nextPayload];
|
|
96
|
+
// Validate payload length
|
|
97
|
+
if (offset > buffer.length) {
|
|
98
|
+
throw new Error(`Payload length exceeds packet size. Offset: ${offset}, Packet size: ${buffer.length}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return new Message(header, payloads);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error instanceof Error) {
|
|
106
|
+
throw new Error(`Failed to parse IKEv2 message: ${error.message}`);
|
|
107
|
+
}
|
|
108
|
+
throw new Error("Failed to parse IKEv2 message: Unknown error");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Serializes IKEv2 message from JSON to Buffer
|
|
113
|
+
* @param {Record<string, any>} json - JSON representation of the message
|
|
114
|
+
* @returns {Buffer}
|
|
115
|
+
*/
|
|
116
|
+
static serializeJSON(json) {
|
|
117
|
+
try {
|
|
118
|
+
// Input validation
|
|
119
|
+
if (!json) {
|
|
120
|
+
throw new Error("JSON data is required");
|
|
121
|
+
}
|
|
122
|
+
if (!json.header) {
|
|
123
|
+
throw new Error("Message header is required");
|
|
124
|
+
}
|
|
125
|
+
if (!Array.isArray(json.payloads)) {
|
|
126
|
+
throw new Error("Message payloads must be an array");
|
|
127
|
+
}
|
|
128
|
+
const header = header_1.Header.serializeJSON(json.header);
|
|
129
|
+
const payloads = json.payloads.map((payload, index) => {
|
|
130
|
+
if (!payload) {
|
|
131
|
+
throw new Error(`Payload at index ${index} is null or undefined`);
|
|
132
|
+
}
|
|
133
|
+
if (typeof payload.type === "undefined") {
|
|
134
|
+
throw new Error(`Payload at index ${index} is missing type field`);
|
|
135
|
+
}
|
|
136
|
+
const type = payload.type;
|
|
137
|
+
const payloadClass = payload_1.payloadTypeMapping[type];
|
|
138
|
+
if (!payloadClass) {
|
|
139
|
+
throw new Error(`Unknown payload type: ${type} at index ${index}`);
|
|
140
|
+
}
|
|
141
|
+
return payloadClass.serializeJSON(payload);
|
|
142
|
+
});
|
|
143
|
+
return Buffer.concat([header, ...payloads]);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (error instanceof Error) {
|
|
147
|
+
throw new Error(`Failed to serialize IKEv2 message: ${error.message}`);
|
|
148
|
+
}
|
|
149
|
+
throw new Error("Failed to serialize IKEv2 message: Unknown error");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Serializes IKEv2 message
|
|
154
|
+
* @returns {Buffer}
|
|
155
|
+
*/
|
|
156
|
+
serialize() {
|
|
157
|
+
// Resolve the nextPayload fields, to ensure that we send a valid message
|
|
158
|
+
if (this.payloads.length > 0) {
|
|
159
|
+
this.header.nextPayload = this.payloads[0].type;
|
|
160
|
+
for (let i = 0; i < this.payloads.length - 1; i++) {
|
|
161
|
+
if (this.payloads[i].type === payload_1.payloadType.SK) {
|
|
162
|
+
throw new Error("SK payload must be the last payload in the message");
|
|
163
|
+
}
|
|
164
|
+
this.payloads[i].nextPayload = this.payloads[i + 1].type;
|
|
165
|
+
}
|
|
166
|
+
if (this.payloads[this.payloads.length - 1].type !== payload_1.payloadType.SK) {
|
|
167
|
+
// The SK payload is special: its nextPayload indicates the first inner payload type. It also must be the
|
|
168
|
+
// last payload in the message.
|
|
169
|
+
this.payloads[this.payloads.length - 1].nextPayload = payload_1.payloadType.NONE;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.header.nextPayload = payload_1.payloadType.NONE;
|
|
174
|
+
}
|
|
175
|
+
// Serialize the payloads first, since we need their lengths to compute the total message length
|
|
176
|
+
const payloadsBuffers = this.payloads.map((payload) => payload.serialize());
|
|
177
|
+
const payloadsBuffer = Buffer.concat(payloadsBuffers);
|
|
178
|
+
// Update header length
|
|
179
|
+
this.header.length = header_1.Header.headerLength + payloadsBuffer.length;
|
|
180
|
+
// Serialize header
|
|
181
|
+
const headerBuffer = this.header.serialize();
|
|
182
|
+
// Concatenate header and payloads
|
|
183
|
+
return Buffer.concat([headerBuffer, payloadsBuffer]);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Returns a JSON representation of the message
|
|
187
|
+
* @returns {Record<string, any>}
|
|
188
|
+
*/
|
|
189
|
+
toJSON() {
|
|
190
|
+
return {
|
|
191
|
+
header: this.header.toJSON(),
|
|
192
|
+
payloads: this.payloads.map((payload) => payload.toJSON()),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns a string representation of the message
|
|
197
|
+
* @returns {string}
|
|
198
|
+
*/
|
|
199
|
+
toString() {
|
|
200
|
+
const header = this.header.toString();
|
|
201
|
+
const payloads = this.payloads
|
|
202
|
+
.map((payload) => payload.toString())
|
|
203
|
+
.join(",\n");
|
|
204
|
+
return `Header:\n ${header}\nPayloads:\n ${payloads}`;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Gets the payloads of the given type
|
|
208
|
+
* @param type
|
|
209
|
+
* @returns Payload[] | undefined
|
|
210
|
+
*/
|
|
211
|
+
getPayloads(type) {
|
|
212
|
+
return this.payloads.filter((payload) => payload.type === type);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Gets the payload of the given type
|
|
216
|
+
* @param type
|
|
217
|
+
* @returns Payload | undefined
|
|
218
|
+
*/
|
|
219
|
+
getPayload(type) {
|
|
220
|
+
var _a;
|
|
221
|
+
return (_a = this.getPayloads(type)) === null || _a === void 0 ? void 0 : _a[0];
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Verifies the integrity checksum data of the given packet using the provided verifyFunction. Should be called on
|
|
225
|
+
* whenever you detect that the SK payload is present.
|
|
226
|
+
*
|
|
227
|
+
* The SK payload is always the last one in the message, such that the Integrity Checksum Data is at the end of the
|
|
228
|
+
* packet.
|
|
229
|
+
*
|
|
230
|
+
* Might want to verify that the SK payload is the last one in the message before calling this function. Else it will
|
|
231
|
+
* always fail.
|
|
232
|
+
*
|
|
233
|
+
* @param iv the IV used for the encryption of the SK payload, obtained from the decrypt function.
|
|
234
|
+
* @param packet the original input packet, as passed to the parse() function
|
|
235
|
+
* @param verifyFunction
|
|
236
|
+
* @returns
|
|
237
|
+
*/
|
|
238
|
+
static verifyIntegrityChecksumData(iv, packet, verifyFunction) {
|
|
239
|
+
let buffer = this.getBuffer(packet);
|
|
240
|
+
if (buffer.length < header_1.Header.headerLength) {
|
|
241
|
+
throw new Error(`Packet too short for Integrity Checksum Data. Expected at least ${header_1.Header.headerLength} bytes, got ${buffer.length}`);
|
|
242
|
+
}
|
|
243
|
+
return verifyFunction(iv, buffer);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Updated the integrity checksum data of the given packet using the provided computeFunction. Should be called on the
|
|
247
|
+
* entire serialized packet, when an SK was included (and it was the last payload, hence the Integrity Checksum Data
|
|
248
|
+
* is supposed to be in the last bytes of the packet).
|
|
249
|
+
*
|
|
250
|
+
* Please verify that the SK payload is the last one in the message before calling this function.
|
|
251
|
+
*
|
|
252
|
+
* @param packet
|
|
253
|
+
* @param integrityChecksumDataLength
|
|
254
|
+
* @param signFunction
|
|
255
|
+
* @returns
|
|
256
|
+
*/
|
|
257
|
+
static updateIntegrityChecksumData(iv, packet, signFunction) {
|
|
258
|
+
if (packet.length < header_1.Header.headerLength) {
|
|
259
|
+
throw new Error(`Packet too short for Integrity Checksum Data. Expected at least ${header_1.Header.headerLength} bytes, got ${packet.length}`);
|
|
260
|
+
}
|
|
261
|
+
return signFunction(iv, packet);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
exports.Message = Message;
|