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.
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Header = exports.exchangeType = void 0;
4
+ const payload_1 = require("./payload");
5
+ /**
6
+ * IKEv2 Message Header
7
+ * 1 2 3
8
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
9
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
10
+ ! IKE_SA Initiator's SPI !
11
+ ! (8 Octets) !
12
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13
+ ! IKE_SA Responder's SPI !
14
+ ! (8 Octets) !
15
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16
+ ! Next Payload(1)! MjVer ! MnVer ! Exchange Type(1) ! Flags(1) !
17
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
18
+ ! Message ID (4 Octets) !
19
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20
+ ! Length (4 Octets) !
21
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
22
+
23
+ RFC 4306: IKE Header Format
24
+ */
25
+ /**
26
+ * IKEv2 Exchange Types: \
27
+ * IKE_SA_INIT = 34 \
28
+ * IKE_AUTH = 35 \
29
+ * CREATE_CHILD_SA = 36 \
30
+ * INFORMATIONAL = 37
31
+ */
32
+ var exchangeType;
33
+ (function (exchangeType) {
34
+ exchangeType[exchangeType["IKE_SA_INIT"] = 34] = "IKE_SA_INIT";
35
+ exchangeType[exchangeType["IKE_AUTH"] = 35] = "IKE_AUTH";
36
+ exchangeType[exchangeType["CREATE_CHILD_SA"] = 36] = "CREATE_CHILD_SA";
37
+ exchangeType[exchangeType["INFORMATIONAL"] = 37] = "INFORMATIONAL";
38
+ })(exchangeType || (exports.exchangeType = exchangeType = {}));
39
+ /**
40
+ * IKEv2 Message Header
41
+ * @class
42
+ * @property {Buffer} initiatorSPI - 8 bytes
43
+ * @property {Buffer} responderSPI - 8 bytes
44
+ * @property {payloadType} nextPayload - 1 byte
45
+ * @property {number} majorVersion - 4 bits
46
+ * @property {number} minorVersion - 4 bits
47
+ * @property {exchangeType} exchangeType - 1 byte
48
+ * @property {boolean} isInitiator - 1 bit (flags)
49
+ * @property {boolean} canUseHigherVersion - 1 bit (flags)
50
+ * @property {boolean} isResponse - 1 bit (flags)
51
+ * @property {number} messageID - 4 bytes
52
+ * @property {number} length - 4 bytes
53
+ */
54
+ class Header {
55
+ constructor(initiatorSPI, responderSPI, nextPayload, majorVersion, minorVersion, exchangeType, isInitiator, canUseHigherVersion, isResponse, messageID, length = 0 // if left as 0, it will be calculated during serialization
56
+ ) {
57
+ this.initiatorSPI = initiatorSPI;
58
+ this.responderSPI = responderSPI;
59
+ this.nextPayload = nextPayload;
60
+ this.majorVersion = majorVersion;
61
+ this.minorVersion = minorVersion;
62
+ this.exchangeType = exchangeType;
63
+ this.isInitiator = isInitiator;
64
+ this.canUseHigherVersion = canUseHigherVersion;
65
+ this.isResponse = isResponse;
66
+ this.messageID = messageID;
67
+ this.length = length;
68
+ // Validate SPI buffers
69
+ if (!Buffer.isBuffer(initiatorSPI) || initiatorSPI.length !== 8) {
70
+ throw new Error("Initiator SPI must be an 8-byte Buffer");
71
+ }
72
+ if (!Buffer.isBuffer(responderSPI) || responderSPI.length !== 8) {
73
+ throw new Error("Responder SPI must be an 8-byte Buffer");
74
+ }
75
+ // Validate version numbers (4-bit each)
76
+ if (majorVersion < 0 || majorVersion > 15) {
77
+ throw new Error(`Major version must be between 0 and 15, got ${majorVersion}`);
78
+ }
79
+ if (minorVersion < 0 || minorVersion > 15) {
80
+ throw new Error(`Minor version must be between 0 and 15, got ${minorVersion}`);
81
+ }
82
+ // Validate messageID (32-bit unsigned integer)
83
+ if (!Number.isInteger(messageID) ||
84
+ messageID < 0 ||
85
+ messageID > 0xffffffff) {
86
+ throw new Error(`Message ID must be a 32-bit unsigned integer (0-4294967295), got ${messageID}`);
87
+ }
88
+ // Validate length (32-bit unsigned integer)
89
+ if (!Number.isInteger(length) || length < 0 || length > 0xffffffff) {
90
+ throw new Error(`Length must be a 32-bit unsigned integer (0-4294967295), got ${length}`);
91
+ }
92
+ // Validate minimum length
93
+ if (length < Header.headerLength && length !== 0) {
94
+ throw new Error(`Length must be at least ${Header.headerLength} bytes, got ${length}`);
95
+ }
96
+ }
97
+ /**
98
+ * Parses IKEv2 message header
99
+ * @param buffer
100
+ * @public
101
+ * @static
102
+ * @returns {Header}
103
+ */
104
+ static parse(buffer) {
105
+ if (!Buffer.isBuffer(buffer)) {
106
+ throw new Error("Input must be a Buffer");
107
+ }
108
+ if (buffer.length < Header.headerLength) {
109
+ throw new Error(`Buffer is too short to contain a valid IKEv2 message header. Expected at least ${Header.headerLength} bytes, got ${buffer.length}`);
110
+ }
111
+ try {
112
+ let offset = 0;
113
+ // Validate we have enough data for each field before accessing
114
+ if (offset + 8 > buffer.length) {
115
+ throw new Error("Buffer too short for initiator SPI");
116
+ }
117
+ const initiatorSPI = buffer.subarray(offset, offset + 8);
118
+ offset += 8;
119
+ if (offset + 8 > buffer.length) {
120
+ throw new Error("Buffer too short for responder SPI");
121
+ }
122
+ const responderSPI = buffer.subarray(offset, offset + 8);
123
+ offset += 8;
124
+ if (offset + 1 > buffer.length) {
125
+ throw new Error("Buffer too short for next payload");
126
+ }
127
+ const nextPayloadByte = buffer.readUInt8(offset);
128
+ const nextPayload = nextPayloadByte;
129
+ offset += 1;
130
+ if (offset + 1 > buffer.length) {
131
+ throw new Error("Buffer too short for version");
132
+ }
133
+ const majorVersion = buffer.readUInt8(offset) >> 4;
134
+ const minorVersion = buffer.readUInt8(offset) & 0x0f;
135
+ offset += 1;
136
+ if (offset + 1 > buffer.length) {
137
+ throw new Error("Buffer too short for exchange type");
138
+ }
139
+ const exchangeTypeByte = buffer.readUInt8(offset);
140
+ const exchangeTypePayload = exchangeTypeByte;
141
+ offset += 1;
142
+ if (offset + 1 > buffer.length) {
143
+ throw new Error("Buffer too short for flags");
144
+ }
145
+ const flags = buffer.readUInt8(offset);
146
+ const isInitiator = Boolean(flags & 0x08);
147
+ const canUseHigherVersion = Boolean(flags & 0x10);
148
+ const isResponse = Boolean(flags & 0x20);
149
+ offset += 1;
150
+ if (offset + 4 > buffer.length) {
151
+ throw new Error("Buffer too short for message ID");
152
+ }
153
+ const messageID = buffer.readUInt32BE(offset);
154
+ offset += 4;
155
+ if (offset + 4 > buffer.length) {
156
+ throw new Error("Buffer too short for length");
157
+ }
158
+ const length = buffer.readUInt32BE(offset);
159
+ offset += 4;
160
+ return new Header(initiatorSPI, responderSPI, nextPayload, majorVersion, minorVersion, exchangeTypePayload, isInitiator, canUseHigherVersion, isResponse, messageID, length);
161
+ }
162
+ catch (error) {
163
+ if (error instanceof Error) {
164
+ throw new Error(`Failed to parse message header: ${error.message}`);
165
+ }
166
+ throw new Error("Failed to parse message header: Unknown error");
167
+ }
168
+ }
169
+ /**
170
+ * Serializes JSON to IKEv2 message header
171
+ * @param json object
172
+ * @public
173
+ * @static
174
+ * @returns {Buffer}
175
+ */
176
+ static serializeJSON(json) {
177
+ var _a;
178
+ try {
179
+ // Input validation
180
+ if (!json) {
181
+ throw new Error("JSON data is required");
182
+ }
183
+ // Validate required fields
184
+ if (!json.initiatorSPI || !json.responderSPI) {
185
+ throw new Error("Both initiatorSPI and responderSPI are required");
186
+ }
187
+ if (typeof json.nextPayload === "undefined") {
188
+ throw new Error("nextPayload is required");
189
+ }
190
+ if (typeof json.exchangeType === "undefined") {
191
+ throw new Error("exchangeType is required");
192
+ }
193
+ if (typeof json.messageID === "undefined") {
194
+ throw new Error("messageID is required");
195
+ }
196
+ if (typeof json.length === "undefined") {
197
+ throw new Error("length is required");
198
+ }
199
+ // Validate flags object
200
+ if (!json.flags || typeof json.flags !== "object") {
201
+ throw new Error("flags object is required");
202
+ }
203
+ const version = (_a = json.version) === null || _a === void 0 ? void 0 : _a.split(".");
204
+ const header = new Header(Buffer.from(json.initiatorSPI, "hex"), Buffer.from(json.responderSPI, "hex"), json.nextPayload, version ? parseInt(version[0], 10) : json.majorVersion, version ? parseInt(version[1], 10) : json.minorVersion, json.exchangeType, json.flags.isInitiator, json.flags.useHigherVersion, json.flags.isResponse, json.messageID, json.length);
205
+ return header.serialize();
206
+ }
207
+ catch (error) {
208
+ if (error instanceof Error) {
209
+ throw new Error(`Failed to serialize header: ${error.message}`);
210
+ }
211
+ throw new Error("Failed to serialize header: Unknown error");
212
+ }
213
+ }
214
+ /**
215
+ * Serializes IKEv2 message header
216
+ * @public
217
+ * @returns {Buffer}
218
+ */
219
+ serialize() {
220
+ const buffer = Buffer.alloc(28);
221
+ let offset = 0;
222
+ // write initiator SPI
223
+ this.initiatorSPI.copy(buffer, offset);
224
+ offset += 8;
225
+ // write responder SPI
226
+ this.responderSPI.copy(buffer, offset);
227
+ offset += 8;
228
+ // Write nextPayload (1 byte)
229
+ buffer.writeUInt8(this.nextPayload, offset);
230
+ offset += 1;
231
+ // Write majorVersion and minorVersion (4 bits each, combined into 1 byte)
232
+ buffer.writeUInt8((this.majorVersion << 4) | (this.minorVersion & 0x0f), offset);
233
+ offset += 1;
234
+ // Write exchangeType (1 byte)
235
+ buffer.writeUInt8(this.exchangeType, offset);
236
+ offset += 1;
237
+ // Write flags (1 byte)
238
+ let flags = 0;
239
+ if (this.isInitiator)
240
+ flags |= 0x08;
241
+ if (this.canUseHigherVersion)
242
+ flags |= 0x10;
243
+ if (this.isResponse)
244
+ flags |= 0x20;
245
+ buffer.writeUInt8(flags, offset);
246
+ offset += 1;
247
+ // Write messageID (4 bytes)
248
+ buffer.writeUInt32BE(this.messageID, offset);
249
+ offset += 4;
250
+ // Write length (4 bytes)
251
+ buffer.writeUInt32BE(this.length, offset);
252
+ return buffer;
253
+ }
254
+ isRequest() {
255
+ return !this.isResponse;
256
+ }
257
+ isResponder() {
258
+ return !this.isInitiator;
259
+ }
260
+ /**
261
+ * Convert object to JSON
262
+ * @method
263
+ * @public
264
+ * @returns {Record<string, any>} JSON object
265
+ */
266
+ toJSON() {
267
+ return {
268
+ initiatorSPI: this.initiatorSPI.toString("hex"),
269
+ responderSPI: this.responderSPI.toString("hex"),
270
+ nextPayload: this.nextPayload,
271
+ version: this.majorVersion + "." + this.minorVersion,
272
+ exchangeType: this.exchangeType,
273
+ flags: {
274
+ isInitiator: this.isInitiator,
275
+ useHigherVersion: this.canUseHigherVersion,
276
+ isResponse: this.isResponse,
277
+ },
278
+ messageID: this.messageID,
279
+ length: this.length,
280
+ };
281
+ }
282
+ /**
283
+ * Returns a string representation of the header
284
+ * @method
285
+ * @public
286
+ * @returns {void}
287
+ */
288
+ toString() {
289
+ const prettyJson = this.toJSON();
290
+ prettyJson.messageID = `0x${prettyJson.messageID.toString(16).padStart(8, "0")}`;
291
+ prettyJson.nextPayload =
292
+ payload_1.payloadType[prettyJson.nextPayload] + " (" + prettyJson.nextPayload + ")";
293
+ prettyJson.exchangeType =
294
+ exchangeType[prettyJson.exchangeType] +
295
+ " (" +
296
+ prettyJson.exchangeType +
297
+ ")";
298
+ prettyJson.flags = {
299
+ isInitiator: prettyJson.flags.isInitiator ? "Initiator" : "Responder",
300
+ useHigherVersion: prettyJson.flags.canUseHigherVersion ? "Yes" : "No",
301
+ isResponse: prettyJson.flags.isResponse ? "Response" : "Request",
302
+ };
303
+ return JSON.stringify(prettyJson, null, 2);
304
+ }
305
+ }
306
+ exports.Header = Header;
307
+ Header.headerLength = 28;
@@ -0,0 +1,9 @@
1
+ export * from "./attribute";
2
+ export * from "./payload";
3
+ export * from "./header";
4
+ export * from "./proposal";
5
+ export * from "./selector";
6
+ export * from "./transform";
7
+ export * from "./message";
8
+ import * as ikev2 from "./message";
9
+ export { ikev2 };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ var __importStar = (this && this.__importStar) || function (mod) {
22
+ if (mod && mod.__esModule) return mod;
23
+ var result = {};
24
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
25
+ __setModuleDefault(result, mod);
26
+ return result;
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.ikev2 = void 0;
30
+ __exportStar(require("./attribute"), exports);
31
+ __exportStar(require("./payload"), exports);
32
+ __exportStar(require("./header"), exports);
33
+ __exportStar(require("./proposal"), exports);
34
+ __exportStar(require("./selector"), exports);
35
+ __exportStar(require("./transform"), exports);
36
+ __exportStar(require("./message"), exports);
37
+ const ikev2 = __importStar(require("./message"));
38
+ exports.ikev2 = ikev2;
@@ -0,0 +1,88 @@
1
+ import { Header } from "./header";
2
+ import { Payload, payloadType } from "./payload";
3
+ /**
4
+ * IKEv2 Message class
5
+ * @class
6
+ * @property {Header} header - IKEv2 header
7
+ * @property {Payload[]} payloads - IKEv2 payloads
8
+ */
9
+ export declare class Message {
10
+ header: Header;
11
+ payloads: Payload[];
12
+ /**
13
+ * @constructor
14
+ * @param {Header} header - IKEv2 header
15
+ * @param {Payload[]} payloads - IKEv2 payloads
16
+ */
17
+ constructor(header: Header, payloads: Payload[]);
18
+ private static getBuffer;
19
+ /**
20
+ * Parses IKEv2 message
21
+ * @param buffer - Buffer containing the message
22
+ * @param headerOnly - If only header is needed (default: false)
23
+ * @returns {Message}
24
+ */
25
+ static parse(packet: Buffer | string, headerOnly?: boolean): Message;
26
+ /**
27
+ * Serializes IKEv2 message from JSON to Buffer
28
+ * @param {Record<string, any>} json - JSON representation of the message
29
+ * @returns {Buffer}
30
+ */
31
+ static serializeJSON(json: Record<string, any>): Buffer;
32
+ /**
33
+ * Serializes IKEv2 message
34
+ * @returns {Buffer}
35
+ */
36
+ serialize(): Buffer;
37
+ /**
38
+ * Returns a JSON representation of the message
39
+ * @returns {Record<string, any>}
40
+ */
41
+ toJSON(): Record<string, any>;
42
+ /**
43
+ * Returns a string representation of the message
44
+ * @returns {string}
45
+ */
46
+ toString(): string;
47
+ /**
48
+ * Gets the payloads of the given type
49
+ * @param type
50
+ * @returns Payload[] | undefined
51
+ */
52
+ getPayloads(type: payloadType): Payload[] | undefined;
53
+ /**
54
+ * Gets the payload of the given type
55
+ * @param type
56
+ * @returns Payload | undefined
57
+ */
58
+ getPayload(type: payloadType): Payload | undefined;
59
+ /**
60
+ * Verifies the integrity checksum data of the given packet using the provided verifyFunction. Should be called on
61
+ * whenever you detect that the SK payload is present.
62
+ *
63
+ * The SK payload is always the last one in the message, such that the Integrity Checksum Data is at the end of the
64
+ * packet.
65
+ *
66
+ * Might want to verify that the SK payload is the last one in the message before calling this function. Else it will
67
+ * always fail.
68
+ *
69
+ * @param iv the IV used for the encryption of the SK payload, obtained from the decrypt function.
70
+ * @param packet the original input packet, as passed to the parse() function
71
+ * @param verifyFunction
72
+ * @returns
73
+ */
74
+ static verifyIntegrityChecksumData(iv: Buffer, packet: Buffer | string, verifyFunction: (iv: Buffer, packet: Buffer) => boolean): boolean;
75
+ /**
76
+ * Updated the integrity checksum data of the given packet using the provided computeFunction. Should be called on the
77
+ * entire serialized packet, when an SK was included (and it was the last payload, hence the Integrity Checksum Data
78
+ * is supposed to be in the last bytes of the packet).
79
+ *
80
+ * Please verify that the SK payload is the last one in the message before calling this function.
81
+ *
82
+ * @param packet
83
+ * @param integrityChecksumDataLength
84
+ * @param signFunction
85
+ * @returns
86
+ */
87
+ static updateIntegrityChecksumData(iv: Buffer, packet: Buffer, signFunction: (iv: Buffer, packet: Buffer) => Buffer): Buffer;
88
+ }