node-rtc-connection 1.0.18 → 2.0.4

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.
Files changed (65) hide show
  1. package/README.md +94 -85
  2. package/dist/index.cjs +20 -5421
  3. package/dist/index.mjs +25 -5413
  4. package/dist/types/crypto/der.d.ts +107 -0
  5. package/dist/types/crypto/x509.d.ts +56 -0
  6. package/dist/types/datachannel/RTCDataChannel.d.ts +179 -0
  7. package/dist/types/dtls/RTCCertificate.d.ts +163 -0
  8. package/dist/types/dtls/cipher.d.ts +81 -0
  9. package/dist/types/dtls/connection.d.ts +81 -0
  10. package/dist/types/dtls/prf.d.ts +29 -0
  11. package/dist/types/dtls/protocol.d.ts +127 -0
  12. package/dist/types/foundation/ByteBufferQueue.d.ts +71 -0
  13. package/dist/types/foundation/RTCError.d.ts +152 -0
  14. package/dist/types/ice/RTCIceCandidate.d.ts +161 -0
  15. package/dist/types/ice/ice-agent.d.ts +154 -0
  16. package/dist/types/ice/stun-message.d.ts +92 -0
  17. package/dist/types/index.d.ts +29 -0
  18. package/dist/types/peerconnection/RTCPeerConnection.d.ts +74 -0
  19. package/dist/types/sctp/association.d.ts +77 -0
  20. package/dist/types/sctp/chunks.d.ts +200 -0
  21. package/dist/types/sctp/crc32c.d.ts +24 -0
  22. package/dist/types/sctp/datachannel-manager.d.ts +51 -0
  23. package/dist/types/sctp/dcep.d.ts +56 -0
  24. package/dist/types/sdp/RTCSessionDescription.d.ts +73 -0
  25. package/dist/types/sdp/sdp-utils.d.ts +103 -0
  26. package/dist/types/stun/stun-client.d.ts +119 -0
  27. package/dist/types/transport-stack.d.ts +68 -0
  28. package/package.json +26 -21
  29. package/src/crypto/der.ts +205 -0
  30. package/src/crypto/x509.ts +146 -0
  31. package/src/datachannel/RTCDataChannel.ts +388 -0
  32. package/src/dtls/RTCCertificate.ts +396 -0
  33. package/src/dtls/cipher.ts +198 -0
  34. package/src/dtls/connection.ts +974 -0
  35. package/src/dtls/prf.ts +62 -0
  36. package/src/dtls/protocol.ts +204 -0
  37. package/src/foundation/{ByteBufferQueue.js → ByteBufferQueue.ts} +74 -72
  38. package/src/foundation/{RTCError.js → RTCError.ts} +110 -60
  39. package/src/ice/{RTCIceCandidate.js → RTCIceCandidate.ts} +140 -92
  40. package/src/ice/ice-agent.ts +609 -0
  41. package/src/ice/stun-message.ts +260 -0
  42. package/src/index.ts +72 -0
  43. package/src/peerconnection/RTCPeerConnection.ts +430 -0
  44. package/src/sctp/association.ts +523 -0
  45. package/src/sctp/chunks.ts +350 -0
  46. package/src/sctp/crc32c.ts +57 -0
  47. package/src/sctp/datachannel-manager.ts +187 -0
  48. package/src/sctp/dcep.ts +94 -0
  49. package/src/sdp/{RTCSessionDescription.js → RTCSessionDescription.ts} +42 -29
  50. package/src/sdp/sdp-utils.ts +229 -0
  51. package/src/stun/stun-client.ts +936 -0
  52. package/src/transport-stack.ts +165 -0
  53. package/dist/index.cjs.map +0 -1
  54. package/dist/index.mjs.map +0 -1
  55. package/src/datachannel/RTCDataChannel.js +0 -354
  56. package/src/dtls/RTCCertificate.js +0 -310
  57. package/src/dtls/RTCDtlsTransport.js +0 -247
  58. package/src/ice/RTCIceTransport.js +0 -998
  59. package/src/index.d.ts +0 -400
  60. package/src/index.js +0 -92
  61. package/src/network/network-transport.js +0 -478
  62. package/src/peerconnection/RTCPeerConnection.js +0 -851
  63. package/src/sctp/RTCSctpTransport.js +0 -253
  64. package/src/sdp/sdp-utils.js +0 -224
  65. package/src/stun/stun-client.js +0 -643
@@ -1,354 +0,0 @@
1
- /**
2
- * @file RTCDataChannel.js
3
- * @description WebRTC DataChannel implementation for peer-to-peer data transfer.
4
- * @module datachannel/RTCDataChannel
5
- *
6
- * Ported from Chromium's RTCDataChannel implementation:
7
- * - cc/rtc_data_channel.h
8
- * - cc/rtc_data_channel.cc
9
- * - cc/rtc_data_channel.idl
10
- */
11
-
12
- const EventEmitter = require('events');
13
-
14
- /**
15
- * RTCDataChannelState - Current state of the data channel
16
- * @readonly
17
- * @enum {string}
18
- */
19
- const RTCDataChannelState = Object.freeze({
20
- CONNECTING: 'connecting',
21
- OPEN: 'open',
22
- CLOSING: 'closing',
23
- CLOSED: 'closed'
24
- });
25
-
26
- /**
27
- * RTCDataChannelInit - Configuration for creating a data channel
28
- * @typedef {Object} RTCDataChannelInit
29
- * @property {boolean} [ordered=true] - Whether messages must arrive in order
30
- * @property {number} [maxPacketLifeTime] - Maximum packet lifetime in milliseconds
31
- * @property {number} [maxRetransmits] - Maximum number of retransmissions
32
- * @property {string} [protocol=''] - Subprotocol name
33
- * @property {boolean} [negotiated=false] - Whether channel was negotiated out-of-band
34
- * @property {number} [id] - Channel ID (required if negotiated is true)
35
- */
36
-
37
- /**
38
- * @class RTCDataChannel
39
- * @extends EventEmitter
40
- * @description Represents a bidirectional data channel between peers.
41
- * Provides reliable or unreliable data transfer with configurable ordering.
42
- *
43
- * Events:
44
- * - 'open': Fired when the channel opens
45
- * - 'message': Fired when a message is received
46
- * - 'bufferedamountlow': Fired when bufferedAmount drops below threshold
47
- * - 'error': Fired when an error occurs
48
- * - 'closing': Fired when the channel is closing
49
- * - 'close': Fired when the channel closes
50
- *
51
- * @example
52
- * const dataChannel = peerConnection.createDataChannel('myChannel', {
53
- * ordered: true,
54
- * maxRetransmits: 3
55
- * });
56
- *
57
- * dataChannel.on('open', () => {
58
- * console.log('Channel opened');
59
- * dataChannel.send('Hello!');
60
- * });
61
- *
62
- * dataChannel.on('message', (event) => {
63
- * console.log('Received:', event.data);
64
- * });
65
- */
66
- class RTCDataChannel extends EventEmitter {
67
- /**
68
- * Create an RTCDataChannel instance.
69
- * @param {string} label - Channel label
70
- * @param {RTCDataChannelInit} [init] - Channel configuration
71
- */
72
- constructor(label, init = {}) {
73
- super();
74
-
75
- if (typeof label !== 'string') {
76
- throw new TypeError('label must be a string');
77
- }
78
-
79
- // Channel configuration
80
- this._label = label;
81
- this._ordered = init.ordered !== undefined ? init.ordered : true;
82
- this._maxPacketLifeTime = init.maxPacketLifeTime || null;
83
- this._maxRetransmits = init.maxRetransmits || null;
84
- this._protocol = init.protocol || '';
85
- this._negotiated = init.negotiated || false;
86
- this._id = init.id !== undefined ? init.id : null;
87
-
88
- // State
89
- this._readyState = RTCDataChannelState.CONNECTING;
90
- this._bufferedAmount = 0;
91
- this._bufferedAmountLowThreshold = 0;
92
- this._binaryType = 'arraybuffer'; // or 'blob'
93
-
94
- // Message queue for when not open
95
- this._messageQueue = [];
96
- }
97
-
98
- /**
99
- * Get the channel label.
100
- * @returns {string} Channel label
101
- */
102
- get label() {
103
- return this._label;
104
- }
105
-
106
- /**
107
- * Check if messages are delivered in order.
108
- * @returns {boolean} True if ordered
109
- */
110
- get ordered() {
111
- return this._ordered;
112
- }
113
-
114
- /**
115
- * Get the maximum packet lifetime in milliseconds.
116
- * @returns {number|null} Maximum lifetime or null if not set
117
- */
118
- get maxPacketLifeTime() {
119
- return this._maxPacketLifeTime;
120
- }
121
-
122
- /**
123
- * Get the maximum number of retransmissions.
124
- * @returns {number|null} Maximum retransmits or null if not set
125
- */
126
- get maxRetransmits() {
127
- return this._maxRetransmits;
128
- }
129
-
130
- /**
131
- * Get the subprotocol name.
132
- * @returns {string} Protocol name
133
- */
134
- get protocol() {
135
- return this._protocol;
136
- }
137
-
138
- /**
139
- * Check if the channel was negotiated out-of-band.
140
- * @returns {boolean} True if negotiated
141
- */
142
- get negotiated() {
143
- return this._negotiated;
144
- }
145
-
146
- /**
147
- * Get the channel ID.
148
- * @returns {number|null} Channel ID or null if not assigned
149
- */
150
- get id() {
151
- return this._id;
152
- }
153
-
154
- /**
155
- * Get the current state of the channel.
156
- * @returns {string} Channel state
157
- */
158
- get readyState() {
159
- return this._readyState;
160
- }
161
-
162
- /**
163
- * Get the number of bytes queued to send.
164
- * @returns {number} Buffered amount in bytes
165
- */
166
- get bufferedAmount() {
167
- return this._bufferedAmount;
168
- }
169
-
170
- /**
171
- * Get the threshold for bufferedamountlow event.
172
- * @returns {number} Threshold in bytes
173
- */
174
- get bufferedAmountLowThreshold() {
175
- return this._bufferedAmountLowThreshold;
176
- }
177
-
178
- /**
179
- * Set the threshold for bufferedamountlow event.
180
- * @param {number} value - Threshold in bytes
181
- */
182
- set bufferedAmountLowThreshold(value) {
183
- this._bufferedAmountLowThreshold = value;
184
- }
185
-
186
- /**
187
- * Get the binary data type.
188
- * @returns {string} 'arraybuffer' or 'blob'
189
- */
190
- get binaryType() {
191
- return this._binaryType;
192
- }
193
-
194
- /**
195
- * Set the binary data type.
196
- * @param {string} value - 'arraybuffer' or 'blob'
197
- * @throws {TypeError} If value is invalid
198
- */
199
- set binaryType(value) {
200
- if (value !== 'arraybuffer' && value !== 'blob') {
201
- throw new TypeError('binaryType must be "arraybuffer" or "blob"');
202
- }
203
- this._binaryType = value;
204
- }
205
-
206
- /**
207
- * Check if the channel is reliable (deprecated).
208
- * @returns {boolean} True if ordered and no packet lifetime/retransmit limits
209
- * @deprecated Use ordered, maxPacketLifeTime, and maxRetransmits instead
210
- */
211
- get reliable() {
212
- return this._ordered &&
213
- this._maxPacketLifeTime === null &&
214
- this._maxRetransmits === null;
215
- }
216
-
217
- /**
218
- * Send a message through the channel.
219
- * @param {string|ArrayBuffer|ArrayBufferView|Blob} data - Data to send
220
- * @throws {Error} If channel is not open or data is invalid
221
- */
222
- send(data) {
223
- if (this._readyState !== RTCDataChannelState.OPEN) {
224
- throw new Error('RTCDataChannel.readyState is not "open"');
225
- }
226
-
227
- let dataToSend;
228
- let byteLength = 0;
229
-
230
- if (typeof data === 'string') {
231
- dataToSend = Buffer.from(data, 'utf8');
232
- byteLength = dataToSend.length;
233
- } else if (data instanceof ArrayBuffer) {
234
- dataToSend = Buffer.from(data);
235
- byteLength = data.byteLength;
236
- } else if (ArrayBuffer.isView(data)) {
237
- dataToSend = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
238
- byteLength = data.byteLength;
239
- } else if (data && typeof data.arrayBuffer === 'function') {
240
- // Blob-like object
241
- throw new Error('Blob sending not yet implemented');
242
- } else {
243
- throw new TypeError('Invalid data type');
244
- }
245
-
246
- // Update buffered amount
247
- this._bufferedAmount += byteLength;
248
-
249
- // Use real network transport
250
- if (!this._send) {
251
- throw new Error('Data channel not connected to network transport');
252
- }
253
-
254
- this._send(data).catch(err => {
255
- console.error('Send error:', err);
256
- this.emit('error', err);
257
- });
258
- }
259
-
260
- /**
261
- * Emit bufferedamountlow if appropriate
262
- * @private
263
- */
264
- _emitBufferedAmountLow() {
265
- if (this._bufferedAmount <= this._bufferedAmountLowThreshold) {
266
- this.emit('bufferedamountlow');
267
- }
268
- }
269
-
270
- /**
271
- * Close the data channel.
272
- */
273
- close() {
274
- if (this._readyState === RTCDataChannelState.CLOSING ||
275
- this._readyState === RTCDataChannelState.CLOSED) {
276
- return;
277
- }
278
-
279
- this._setState(RTCDataChannelState.CLOSING);
280
-
281
- // Transition to closed asynchronously
282
- setImmediate(() => {
283
- if (this._readyState === RTCDataChannelState.CLOSING) {
284
- this._setState(RTCDataChannelState.CLOSED);
285
- }
286
- });
287
- }
288
-
289
- /**
290
- * Set the channel state and emit appropriate events.
291
- * @param {string} newState - New state
292
- * @private
293
- */
294
- _setState(newState) {
295
- const oldState = this._readyState;
296
- if (oldState === newState) {
297
- return;
298
- }
299
-
300
- this._readyState = newState;
301
-
302
- // Emit state-specific events
303
- if (newState === RTCDataChannelState.OPEN) {
304
- this.emit('open');
305
- } else if (newState === RTCDataChannelState.CLOSING) {
306
- this.emit('closing');
307
- } else if (newState === RTCDataChannelState.CLOSED) {
308
- this.emit('close');
309
- }
310
- }
311
-
312
- /**
313
- * Set channel to open state (internal use).
314
- * @private
315
- */
316
- _setStateToOpen() {
317
- this._setState(RTCDataChannelState.OPEN);
318
- }
319
-
320
- /**
321
- * Handle received message (internal use).
322
- * @param {Buffer|string} data - Received data
323
- * @private
324
- */
325
- _onMessage(data) {
326
- const event = {
327
- data: data
328
- };
329
- this.emit('message', event);
330
- }
331
-
332
- /**
333
- * Receive message from network transport (internal use).
334
- * @param {any} data - Received data
335
- * @private
336
- */
337
- _receiveMessage(data) {
338
- this._onMessage(data);
339
- }
340
-
341
- /**
342
- * Set the channel ID (internal use).
343
- * @param {number} id - Channel ID
344
- * @private
345
- */
346
- _setId(id) {
347
- this._id = id;
348
- }
349
- }
350
-
351
- module.exports = {
352
- RTCDataChannel,
353
- RTCDataChannelState
354
- };
@@ -1,310 +0,0 @@
1
- /**
2
- * @file RTCCertificate.js
3
- * @description DTLS certificate implementation for WebRTC.
4
- * @module dtls/RTCCertificate
5
- *
6
- * Ported from Chromium's RTCCertificate implementation:
7
- * - cc/rtc_certificate.h
8
- * - cc/rtc_certificate.cc
9
- * - cc/rtc_certificate.idl
10
- * - cc/rtc_certificate_generator.h
11
- * - cc/rtc_certificate_generator.cc
12
- */
13
-
14
- const crypto = require('crypto');
15
-
16
- /**
17
- * RTCDtlsFingerprint - DTLS certificate fingerprint
18
- * @typedef {Object} RTCDtlsFingerprint
19
- * @property {string} algorithm - Hash algorithm (e.g., 'sha-256')
20
- * @property {string} value - Fingerprint value (colon-separated hex)
21
- */
22
-
23
- /**
24
- * Generate a self-signed certificate for DTLS.
25
- * @param {Object} options - Certificate generation options
26
- * @param {string} [options.name='webrtc'] - Common name for the certificate
27
- * @param {number} [options.days=30] - Days until expiration
28
- * @param {string} [options.hash='sha256'] - Hash algorithm
29
- * @returns {Object} Certificate object with key and cert
30
- * @private
31
- */
32
- function generateSelfSignedCertificate(options = {}) {
33
- const {
34
- name = 'webrtc',
35
- days = 30,
36
- hash = 'sha256'
37
- } = options;
38
-
39
- // Generate RSA key pair
40
- const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
41
- modulusLength: 2048,
42
- publicKeyEncoding: {
43
- type: 'spki',
44
- format: 'pem'
45
- },
46
- privateKeyEncoding: {
47
- type: 'pkcs8',
48
- format: 'pem'
49
- }
50
- });
51
-
52
- // Create certificate using Node.js crypto
53
- // Note: Node.js doesn't have built-in self-signed cert generation,
54
- // so we'll use a simplified approach storing the key pair
55
- const expirationDate = new Date();
56
- expirationDate.setDate(expirationDate.getDate() + days);
57
-
58
- return {
59
- privateKey,
60
- publicKey,
61
- expires: expirationDate.getTime(),
62
- hash
63
- };
64
- }
65
-
66
- /**
67
- * Calculate fingerprint from public key.
68
- * @param {string} publicKey - PEM-encoded public key
69
- * @param {string} algorithm - Hash algorithm (e.g., 'sha256')
70
- * @returns {string} Fingerprint (colon-separated hex)
71
- * @private
72
- */
73
- function calculateFingerprint(publicKey, algorithm = 'sha256') {
74
- // Remove PEM headers and decode base64
75
- const pemBody = publicKey
76
- .replace(/-----BEGIN PUBLIC KEY-----/, '')
77
- .replace(/-----END PUBLIC KEY-----/, '')
78
- .replace(/\s/g, '');
79
-
80
- const keyBuffer = Buffer.from(pemBody, 'base64');
81
-
82
- // Calculate hash
83
- const hash = crypto.createHash(algorithm);
84
- hash.update(keyBuffer);
85
- const digest = hash.digest('hex').toUpperCase();
86
-
87
- // Format as colon-separated pairs
88
- return digest.match(/.{2}/g).join(':');
89
- }
90
-
91
- /**
92
- * @class RTCCertificate
93
- * @description Represents a certificate used for DTLS in WebRTC.
94
- * The certificate includes a key pair and expiration time.
95
- *
96
- * @example
97
- * // Generate a certificate
98
- * const cert = await RTCCertificate.generateCertificate();
99
- * console.log('Expires:', new Date(cert.expires));
100
- * console.log('Fingerprints:', cert.getFingerprints());
101
- *
102
- * @example
103
- * // Generate with custom expiration
104
- * const cert = await RTCCertificate.generateCertificate({
105
- * name: 'my-peer',
106
- * expires: Date.now() + (90 * 24 * 60 * 60 * 1000) // 90 days
107
- * });
108
- */
109
- class RTCCertificate {
110
- /**
111
- * Create an RTCCertificate instance.
112
- * Use generateCertificate() static method instead of calling directly.
113
- * @param {Object} certData - Internal certificate data
114
- * @private
115
- */
116
- constructor(certData) {
117
- // Store certificate data
118
- this._privateKey = certData.privateKey;
119
- this._publicKey = certData.publicKey;
120
- this._expires = certData.expires;
121
- this._hash = certData.hash || 'sha256';
122
-
123
- // Cache fingerprints
124
- this._fingerprints = null;
125
- }
126
-
127
- /**
128
- * Get the expiration time.
129
- * @returns {number} Expiration time in milliseconds since epoch (DOMTimeStamp)
130
- */
131
- get expires() {
132
- return this._expires;
133
- }
134
-
135
- /**
136
- * Get the certificate fingerprints.
137
- * Returns an array of fingerprints for the certificate chain.
138
- * For self-signed certificates, this returns a single fingerprint.
139
- *
140
- * @returns {Array<RTCDtlsFingerprint>} Array of fingerprint objects
141
- */
142
- getFingerprints() {
143
- if (!this._fingerprints) {
144
- // Calculate fingerprint for multiple algorithms
145
- const algorithms = ['sha-256', 'sha-384', 'sha-512'];
146
- this._fingerprints = algorithms.map(algorithm => {
147
- const hashAlgo = algorithm.replace('-', ''); // 'sha-256' -> 'sha256'
148
- return {
149
- algorithm,
150
- value: calculateFingerprint(this._publicKey, hashAlgo)
151
- };
152
- });
153
- }
154
-
155
- return this._fingerprints.map(fp => ({ ...fp }));
156
- }
157
-
158
- /**
159
- * Get the private key in PEM format.
160
- * @returns {string} PEM-encoded private key
161
- * @internal
162
- */
163
- getPrivateKey() {
164
- return this._privateKey;
165
- }
166
-
167
- /**
168
- * Get the public key in PEM format.
169
- * @returns {string} PEM-encoded public key
170
- * @internal
171
- */
172
- getPublicKey() {
173
- return this._publicKey;
174
- }
175
-
176
- /**
177
- * Convert to PEM format (for serialization/storage).
178
- * @returns {Object} Object with pemPrivateKey and pemCertificate
179
- */
180
- toPEM() {
181
- return {
182
- pemPrivateKey: this._privateKey,
183
- pemCertificate: this._publicKey
184
- };
185
- }
186
-
187
- /**
188
- * Check if the certificate has expired.
189
- * @returns {boolean} True if expired, false otherwise
190
- */
191
- isExpired() {
192
- return Date.now() > this._expires;
193
- }
194
-
195
- /**
196
- * Generate a new RTCCertificate asynchronously.
197
- *
198
- * @param {Object} [options] - Generation options
199
- * @param {string} [options.name='webrtc'] - Common name for the certificate
200
- * @param {number} [options.expires] - Expiration time in ms (default: 30 days from now)
201
- * @param {string} [options.hash='sha256'] - Hash algorithm
202
- * @returns {Promise<RTCCertificate>} Promise resolving to generated certificate
203
- *
204
- * @example
205
- * const cert = await RTCCertificate.generateCertificate({
206
- * name: 'my-app',
207
- * expires: Date.now() + (90 * 24 * 60 * 60 * 1000) // 90 days
208
- * });
209
- */
210
- static async generateCertificate(options = {}) {
211
- return new Promise((resolve, reject) => {
212
- try {
213
- // Calculate expiration
214
- let expires;
215
- if (options.expires) {
216
- expires = options.expires;
217
- } else {
218
- const days = options.days || 30;
219
- expires = Date.now() + (days * 24 * 60 * 60 * 1000);
220
- }
221
-
222
- // Generate certificate in next tick to avoid blocking
223
- setImmediate(() => {
224
- try {
225
- const certData = generateSelfSignedCertificate({
226
- name: options.name || 'webrtc',
227
- days: Math.ceil((expires - Date.now()) / (24 * 60 * 60 * 1000)),
228
- hash: options.hash || 'sha256'
229
- });
230
-
231
- certData.expires = expires;
232
- const certificate = new RTCCertificate(certData);
233
- resolve(certificate);
234
- } catch (err) {
235
- reject(err);
236
- }
237
- });
238
- } catch (err) {
239
- reject(err);
240
- }
241
- });
242
- }
243
-
244
- /**
245
- * Create a certificate from PEM strings.
246
- *
247
- * @param {string} pemPrivateKey - PEM-encoded private key
248
- * @param {string} pemCertificate - PEM-encoded certificate (or public key)
249
- * @param {number} [expires] - Expiration time in ms (default: 30 days from now)
250
- * @returns {RTCCertificate} Certificate instance
251
- *
252
- * @example
253
- * const cert = RTCCertificate.fromPEM(
254
- * privateKeyPEM,
255
- * publicKeyPEM,
256
- * Date.now() + (30 * 24 * 60 * 60 * 1000)
257
- * );
258
- */
259
- static fromPEM(pemPrivateKey, pemCertificate, expires) {
260
- if (typeof pemPrivateKey !== 'string' || pemPrivateKey.length === 0) {
261
- throw new TypeError('pemPrivateKey must be a non-empty string');
262
- }
263
-
264
- if (typeof pemCertificate !== 'string' || pemCertificate.length === 0) {
265
- throw new TypeError('pemCertificate must be a non-empty string');
266
- }
267
-
268
- // Default expiration to 30 days if not provided
269
- const expirationTime = expires || (Date.now() + (30 * 24 * 60 * 60 * 1000));
270
-
271
- return new RTCCertificate({
272
- privateKey: pemPrivateKey,
273
- publicKey: pemCertificate,
274
- expires: expirationTime,
275
- hash: 'sha256'
276
- });
277
- }
278
-
279
- /**
280
- * Check if key parameters are supported.
281
- * Currently supports RSA with 1024-4096 bits and ECDSA.
282
- *
283
- * @param {Object} keyParams - Key parameters
284
- * @param {string} keyParams.type - Key type ('RSA' or 'ECDSA')
285
- * @param {number} [keyParams.rsaModulusLength] - RSA key size in bits
286
- * @param {string} [keyParams.namedCurve] - ECDSA curve name
287
- * @returns {boolean} True if supported, false otherwise
288
- */
289
- static isSupportedKeyParams(keyParams) {
290
- if (!keyParams || typeof keyParams !== 'object') {
291
- return false;
292
- }
293
-
294
- if (keyParams.type === 'RSA') {
295
- const modulusLength = keyParams.rsaModulusLength || 2048;
296
- // Support 1024 to 4096 bits
297
- return modulusLength >= 1024 && modulusLength <= 4096;
298
- }
299
-
300
- if (keyParams.type === 'ECDSA') {
301
- // Support common ECDSA curves
302
- const curve = keyParams.namedCurve;
303
- return ['P-256', 'P-384', 'P-521'].includes(curve);
304
- }
305
-
306
- return false;
307
- }
308
- }
309
-
310
- module.exports = RTCCertificate;