@agentdance/node-webrtc-dtls 1.0.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,544 @@
1
+ // DTLS Handshake message types and encode/decode (RFC 6347 Section 4.2)
2
+ //
3
+ // Handshake message header (12 bytes):
4
+ // 1 byte HandshakeType
5
+ // 3 bytes length
6
+ // 2 bytes message_seq
7
+ // 3 bytes fragment_offset
8
+ // 3 bytes fragment_length
9
+ // [body]
10
+
11
+ import { DTLS_VERSION_1_2, type DtlsVersion } from './types.js';
12
+
13
+ export enum HandshakeType {
14
+ HelloRequest = 0,
15
+ ClientHello = 1,
16
+ ServerHello = 2,
17
+ HelloVerifyRequest = 3,
18
+ Certificate = 11,
19
+ ServerKeyExchange = 12,
20
+ CertificateRequest = 13,
21
+ ServerHelloDone = 14,
22
+ CertificateVerify = 15,
23
+ ClientKeyExchange = 16,
24
+ Finished = 20,
25
+ }
26
+
27
+ export interface HandshakeMessage {
28
+ msgType: HandshakeType;
29
+ length: number;
30
+ messageSeq: number;
31
+ fragmentOffset: number;
32
+ fragmentLength: number;
33
+ body: Buffer;
34
+ }
35
+
36
+ export interface TlsExtension {
37
+ type: number;
38
+ data: Buffer;
39
+ }
40
+
41
+ export interface ClientHello {
42
+ clientVersion: DtlsVersion;
43
+ random: Buffer; // 32 bytes
44
+ sessionId: Buffer; // 0-32 bytes
45
+ cookie: Buffer; // 0-255 bytes
46
+ cipherSuites: number[]; // 2 bytes each
47
+ compressionMethods: number[];
48
+ extensions: TlsExtension[];
49
+ }
50
+
51
+ export interface ServerHello {
52
+ serverVersion: DtlsVersion;
53
+ random: Buffer;
54
+ sessionId: Buffer;
55
+ cipherSuite: number;
56
+ compressionMethod: number;
57
+ extensions: TlsExtension[];
58
+ }
59
+
60
+ export interface HelloVerifyRequest {
61
+ serverVersion: DtlsVersion;
62
+ cookie: Buffer;
63
+ }
64
+
65
+ export interface ServerKeyExchange {
66
+ // ECParameters curve_params (named_curve, 2 bytes)
67
+ // ECPoint public_key (1-byte length + bytes)
68
+ // Signature signature (2-byte hash/sig algos + 2-byte length + bytes)
69
+ curveType: number; // 3 = named_curve
70
+ namedCurve: number; // 23 = secp256r1 / P-256
71
+ publicKey: Buffer; // uncompressed EC point (65 bytes)
72
+ signatureAlgorithm: { hash: number; signature: number };
73
+ signature: Buffer;
74
+ }
75
+
76
+ export interface ClientKeyExchange {
77
+ publicKey: Buffer; // uncompressed EC point
78
+ }
79
+
80
+ // Cipher suite values
81
+ export const CipherSuites = {
82
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: 0xc02b,
83
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: 0xc02f,
84
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: 0xc02c,
85
+ } as const;
86
+
87
+ // Extension type constants
88
+ export const ExtensionType = {
89
+ UseSrtp: 0x000e,
90
+ SupportedGroups: 0x000a, // formerly elliptic_curves
91
+ EcPointFormats: 0x000b,
92
+ SignatureAlgorithms: 0x000d,
93
+ RenegotiationInfo: 0xff01,
94
+ } as const;
95
+
96
+ // Named curves
97
+ export const NamedCurve = {
98
+ secp256r1: 23,
99
+ secp384r1: 24,
100
+ } as const;
101
+
102
+ // SRTP protection profiles (RFC 5764)
103
+ export const SrtpProtectionProfile = {
104
+ SRTP_AES128_CM_SHA1_80: 0x0001,
105
+ SRTP_AES128_CM_SHA1_32: 0x0002,
106
+ } as const;
107
+
108
+ // ─── Handshake Message ────────────────────────────────────────────────────────
109
+
110
+ const HANDSHAKE_HEADER_SIZE = 12;
111
+
112
+ export function encodeHandshakeMessage(msg: HandshakeMessage): Buffer {
113
+ const body = msg.body;
114
+ const buf = Buffer.allocUnsafe(HANDSHAKE_HEADER_SIZE + body.length);
115
+ let off = 0;
116
+
117
+ buf.writeUInt8(msg.msgType, off);
118
+ off += 1;
119
+ // 3-byte length
120
+ writeUInt24BE(buf, off, body.length);
121
+ off += 3;
122
+ buf.writeUInt16BE(msg.messageSeq, off);
123
+ off += 2;
124
+ // 3-byte fragment offset
125
+ writeUInt24BE(buf, off, msg.fragmentOffset);
126
+ off += 3;
127
+ // 3-byte fragment length
128
+ writeUInt24BE(buf, off, body.length);
129
+ off += 3;
130
+
131
+ body.copy(buf, off);
132
+ return buf;
133
+ }
134
+
135
+ export function decodeHandshakeMessage(buf: Buffer): HandshakeMessage {
136
+ if (buf.length < HANDSHAKE_HEADER_SIZE) {
137
+ throw new Error(`Buffer too small for handshake message: ${buf.length}`);
138
+ }
139
+ let off = 0;
140
+ const msgType = buf.readUInt8(off) as HandshakeType;
141
+ off += 1;
142
+ const length = readUInt24BE(buf, off);
143
+ off += 3;
144
+ const messageSeq = buf.readUInt16BE(off);
145
+ off += 2;
146
+ const fragmentOffset = readUInt24BE(buf, off);
147
+ off += 3;
148
+ const fragmentLength = readUInt24BE(buf, off);
149
+ off += 3;
150
+
151
+ if (buf.length < off + fragmentLength) {
152
+ throw new Error('Handshake message body truncated');
153
+ }
154
+ const body = Buffer.from(buf.subarray(off, off + fragmentLength));
155
+
156
+ return { msgType, length, messageSeq, fragmentOffset, fragmentLength, body };
157
+ }
158
+
159
+ // ─── ClientHello ──────────────────────────────────────────────────────────────
160
+
161
+ export function encodeClientHello(hello: ClientHello): Buffer {
162
+ const parts: Buffer[] = [];
163
+
164
+ // Version (2 bytes)
165
+ const ver = Buffer.allocUnsafe(2);
166
+ ver.writeUInt8(hello.clientVersion.major, 0);
167
+ ver.writeUInt8(hello.clientVersion.minor, 1);
168
+ parts.push(ver);
169
+
170
+ // Random (32 bytes)
171
+ parts.push(hello.random);
172
+
173
+ // Session ID
174
+ const sid = Buffer.allocUnsafe(1 + hello.sessionId.length);
175
+ sid.writeUInt8(hello.sessionId.length, 0);
176
+ hello.sessionId.copy(sid, 1);
177
+ parts.push(sid);
178
+
179
+ // Cookie
180
+ const ck = Buffer.allocUnsafe(1 + hello.cookie.length);
181
+ ck.writeUInt8(hello.cookie.length, 0);
182
+ hello.cookie.copy(ck, 1);
183
+ parts.push(ck);
184
+
185
+ // Cipher suites
186
+ const csLen = hello.cipherSuites.length * 2;
187
+ const cs = Buffer.allocUnsafe(2 + csLen);
188
+ cs.writeUInt16BE(csLen, 0);
189
+ for (let i = 0; i < hello.cipherSuites.length; i++) {
190
+ cs.writeUInt16BE(hello.cipherSuites[i]!, 2 + i * 2);
191
+ }
192
+ parts.push(cs);
193
+
194
+ // Compression methods
195
+ const cm = Buffer.allocUnsafe(1 + hello.compressionMethods.length);
196
+ cm.writeUInt8(hello.compressionMethods.length, 0);
197
+ for (let i = 0; i < hello.compressionMethods.length; i++) {
198
+ cm.writeUInt8(hello.compressionMethods[i]!, 1 + i);
199
+ }
200
+ parts.push(cm);
201
+
202
+ // Extensions
203
+ if (hello.extensions.length > 0) {
204
+ const extsBuf = encodeExtensions(hello.extensions);
205
+ parts.push(extsBuf);
206
+ }
207
+
208
+ return Buffer.concat(parts);
209
+ }
210
+
211
+ export function decodeClientHello(buf: Buffer): ClientHello {
212
+ let off = 0;
213
+
214
+ const major = buf.readUInt8(off);
215
+ off += 1;
216
+ const minor = buf.readUInt8(off);
217
+ off += 1;
218
+
219
+ const random = Buffer.from(buf.subarray(off, off + 32));
220
+ off += 32;
221
+
222
+ const sidLen = buf.readUInt8(off);
223
+ off += 1;
224
+ const sessionId = Buffer.from(buf.subarray(off, off + sidLen));
225
+ off += sidLen;
226
+
227
+ const cookieLen = buf.readUInt8(off);
228
+ off += 1;
229
+ const cookie = Buffer.from(buf.subarray(off, off + cookieLen));
230
+ off += cookieLen;
231
+
232
+ const csLen = buf.readUInt16BE(off);
233
+ off += 2;
234
+ const cipherSuites: number[] = [];
235
+ for (let i = 0; i < csLen; i += 2) {
236
+ cipherSuites.push(buf.readUInt16BE(off + i));
237
+ }
238
+ off += csLen;
239
+
240
+ const cmLen = buf.readUInt8(off);
241
+ off += 1;
242
+ const compressionMethods: number[] = [];
243
+ for (let i = 0; i < cmLen; i++) {
244
+ compressionMethods.push(buf.readUInt8(off + i));
245
+ }
246
+ off += cmLen;
247
+
248
+ let extensions: TlsExtension[] = [];
249
+ if (off < buf.length) {
250
+ extensions = decodeExtensions(buf, off);
251
+ }
252
+
253
+ return {
254
+ clientVersion: { major, minor },
255
+ random,
256
+ sessionId,
257
+ cookie,
258
+ cipherSuites,
259
+ compressionMethods,
260
+ extensions,
261
+ };
262
+ }
263
+
264
+ // ─── ServerHello ──────────────────────────────────────────────────────────────
265
+
266
+ export function encodeServerHello(hello: ServerHello): Buffer {
267
+ const parts: Buffer[] = [];
268
+
269
+ const ver = Buffer.allocUnsafe(2);
270
+ ver.writeUInt8(hello.serverVersion.major, 0);
271
+ ver.writeUInt8(hello.serverVersion.minor, 1);
272
+ parts.push(ver);
273
+
274
+ parts.push(hello.random);
275
+
276
+ const sid = Buffer.allocUnsafe(1 + hello.sessionId.length);
277
+ sid.writeUInt8(hello.sessionId.length, 0);
278
+ hello.sessionId.copy(sid, 1);
279
+ parts.push(sid);
280
+
281
+ const cs = Buffer.allocUnsafe(2);
282
+ cs.writeUInt16BE(hello.cipherSuite, 0);
283
+ parts.push(cs);
284
+
285
+ const cm = Buffer.allocUnsafe(1);
286
+ cm.writeUInt8(hello.compressionMethod, 0);
287
+ parts.push(cm);
288
+
289
+ if (hello.extensions.length > 0) {
290
+ parts.push(encodeExtensions(hello.extensions));
291
+ }
292
+
293
+ return Buffer.concat(parts);
294
+ }
295
+
296
+ export function decodeServerHello(buf: Buffer): ServerHello {
297
+ let off = 0;
298
+
299
+ const major = buf.readUInt8(off);
300
+ off += 1;
301
+ const minor = buf.readUInt8(off);
302
+ off += 1;
303
+
304
+ const random = Buffer.from(buf.subarray(off, off + 32));
305
+ off += 32;
306
+
307
+ const sidLen = buf.readUInt8(off);
308
+ off += 1;
309
+ const sessionId = Buffer.from(buf.subarray(off, off + sidLen));
310
+ off += sidLen;
311
+
312
+ const cipherSuite = buf.readUInt16BE(off);
313
+ off += 2;
314
+ const compressionMethod = buf.readUInt8(off);
315
+ off += 1;
316
+
317
+ let extensions: TlsExtension[] = [];
318
+ if (off < buf.length) {
319
+ extensions = decodeExtensions(buf, off);
320
+ }
321
+
322
+ return {
323
+ serverVersion: { major, minor },
324
+ random,
325
+ sessionId,
326
+ cipherSuite,
327
+ compressionMethod,
328
+ extensions,
329
+ };
330
+ }
331
+
332
+ // ─── HelloVerifyRequest ───────────────────────────────────────────────────────
333
+
334
+ export function encodeHelloVerifyRequest(hvr: HelloVerifyRequest): Buffer {
335
+ const buf = Buffer.allocUnsafe(3 + hvr.cookie.length);
336
+ buf.writeUInt8(hvr.serverVersion.major, 0);
337
+ buf.writeUInt8(hvr.serverVersion.minor, 1);
338
+ buf.writeUInt8(hvr.cookie.length, 2);
339
+ hvr.cookie.copy(buf, 3);
340
+ return buf;
341
+ }
342
+
343
+ export function decodeHelloVerifyRequest(buf: Buffer): HelloVerifyRequest {
344
+ const major = buf.readUInt8(0);
345
+ const minor = buf.readUInt8(1);
346
+ const cookieLen = buf.readUInt8(2);
347
+ const cookie = Buffer.from(buf.subarray(3, 3 + cookieLen));
348
+ return { serverVersion: { major, minor }, cookie };
349
+ }
350
+
351
+ // ─── ServerKeyExchange ────────────────────────────────────────────────────────
352
+
353
+ export function encodeServerKeyExchange(ske: ServerKeyExchange): Buffer {
354
+ // curve_type (1) + named_curve (2) + point_len (1) + point + hash_algo (1) + sig_algo (1) + sig_len (2) + sig
355
+ const pkLen = ske.publicKey.length;
356
+ const sigLen = ske.signature.length;
357
+ const buf = Buffer.allocUnsafe(1 + 2 + 1 + pkLen + 1 + 1 + 2 + sigLen);
358
+ let off = 0;
359
+ buf.writeUInt8(ske.curveType, off++);
360
+ buf.writeUInt16BE(ske.namedCurve, off);
361
+ off += 2;
362
+ buf.writeUInt8(pkLen, off++);
363
+ ske.publicKey.copy(buf, off);
364
+ off += pkLen;
365
+ buf.writeUInt8(ske.signatureAlgorithm.hash, off++);
366
+ buf.writeUInt8(ske.signatureAlgorithm.signature, off++);
367
+ buf.writeUInt16BE(sigLen, off);
368
+ off += 2;
369
+ ske.signature.copy(buf, off);
370
+ return buf;
371
+ }
372
+
373
+ export function decodeServerKeyExchange(buf: Buffer): ServerKeyExchange {
374
+ let off = 0;
375
+ const curveType = buf.readUInt8(off++);
376
+ const namedCurve = buf.readUInt16BE(off);
377
+ off += 2;
378
+ const pkLen = buf.readUInt8(off++);
379
+ const publicKey = Buffer.from(buf.subarray(off, off + pkLen));
380
+ off += pkLen;
381
+ const hash = buf.readUInt8(off++);
382
+ const signature_ = buf.readUInt8(off++);
383
+ const sigLen = buf.readUInt16BE(off);
384
+ off += 2;
385
+ const signature = Buffer.from(buf.subarray(off, off + sigLen));
386
+ return {
387
+ curveType,
388
+ namedCurve,
389
+ publicKey,
390
+ signatureAlgorithm: { hash, signature: signature_ },
391
+ signature,
392
+ };
393
+ }
394
+
395
+ // ─── Certificate ──────────────────────────────────────────────────────────────
396
+
397
+ export function encodeCertificate(certDer: Buffer): Buffer {
398
+ // CertificateList: 3-byte list length, then each cert: 3-byte cert length + DER
399
+ const certLen = certDer.length;
400
+ const listLen = 3 + certLen;
401
+ const buf = Buffer.allocUnsafe(3 + listLen);
402
+ writeUInt24BE(buf, 0, listLen);
403
+ writeUInt24BE(buf, 3, certLen);
404
+ certDer.copy(buf, 6);
405
+ return buf;
406
+ }
407
+
408
+ export function decodeCertificate(buf: Buffer): Buffer[] {
409
+ let off = 0;
410
+ const listLen = readUInt24BE(buf, off);
411
+ off += 3;
412
+ const end = off + listLen;
413
+ const certs: Buffer[] = [];
414
+ while (off < end) {
415
+ const certLen = readUInt24BE(buf, off);
416
+ off += 3;
417
+ certs.push(Buffer.from(buf.subarray(off, off + certLen)));
418
+ off += certLen;
419
+ }
420
+ return certs;
421
+ }
422
+
423
+ // ─── ClientKeyExchange ────────────────────────────────────────────────────────
424
+
425
+ export function encodeClientKeyExchange(cke: ClientKeyExchange): Buffer {
426
+ const buf = Buffer.allocUnsafe(1 + cke.publicKey.length);
427
+ buf.writeUInt8(cke.publicKey.length, 0);
428
+ cke.publicKey.copy(buf, 1);
429
+ return buf;
430
+ }
431
+
432
+ export function decodeClientKeyExchange(buf: Buffer): ClientKeyExchange {
433
+ const pkLen = buf.readUInt8(0);
434
+ const publicKey = Buffer.from(buf.subarray(1, 1 + pkLen));
435
+ return { publicKey };
436
+ }
437
+
438
+ // ─── Extensions ───────────────────────────────────────────────────────────────
439
+
440
+ function encodeExtensions(extensions: TlsExtension[]): Buffer {
441
+ const parts: Buffer[] = [];
442
+ for (const ext of extensions) {
443
+ const hdr = Buffer.allocUnsafe(4);
444
+ hdr.writeUInt16BE(ext.type, 0);
445
+ hdr.writeUInt16BE(ext.data.length, 2);
446
+ parts.push(hdr);
447
+ parts.push(ext.data);
448
+ }
449
+ const extsBuf = Buffer.concat(parts);
450
+ const len = Buffer.allocUnsafe(2);
451
+ len.writeUInt16BE(extsBuf.length, 0);
452
+ return Buffer.concat([len, extsBuf]);
453
+ }
454
+
455
+ function decodeExtensions(buf: Buffer, off: number): TlsExtension[] {
456
+ if (off + 2 > buf.length) return [];
457
+ const totalLen = buf.readUInt16BE(off);
458
+ off += 2;
459
+ const end = off + totalLen;
460
+ const extensions: TlsExtension[] = [];
461
+ while (off < end && off + 4 <= buf.length) {
462
+ const type = buf.readUInt16BE(off);
463
+ off += 2;
464
+ const dataLen = buf.readUInt16BE(off);
465
+ off += 2;
466
+ const data = Buffer.from(buf.subarray(off, off + dataLen));
467
+ off += dataLen;
468
+ extensions.push({ type, data });
469
+ }
470
+ return extensions;
471
+ }
472
+
473
+ // ─── Extension builders ───────────────────────────────────────────────────────
474
+
475
+ /**
476
+ * Build use_srtp extension data (RFC 5764)
477
+ * protection_profiles: list of 2-byte profile IDs
478
+ */
479
+ export function buildUseSrtpExtension(profiles: number[]): Buffer {
480
+ // 2-byte profiles list length + 2 bytes each + 1 byte MKI length (0)
481
+ const listLen = profiles.length * 2;
482
+ const buf = Buffer.allocUnsafe(2 + listLen + 1);
483
+ buf.writeUInt16BE(listLen, 0);
484
+ for (let i = 0; i < profiles.length; i++) {
485
+ buf.writeUInt16BE(profiles[i]!, 2 + i * 2);
486
+ }
487
+ buf.writeUInt8(0, 2 + listLen); // MKI length = 0
488
+ return buf;
489
+ }
490
+
491
+ export function parseSrtpProfiles(data: Buffer): number[] {
492
+ if (data.length < 2) return [];
493
+ const listLen = data.readUInt16BE(0);
494
+ const profiles: number[] = [];
495
+ for (let i = 0; i < listLen; i += 2) {
496
+ if (2 + i + 1 < data.length) {
497
+ profiles.push(data.readUInt16BE(2 + i));
498
+ }
499
+ }
500
+ return profiles;
501
+ }
502
+
503
+ /**
504
+ * Build supported_groups extension (named curves)
505
+ */
506
+ export function buildSupportedGroupsExtension(curves: number[]): Buffer {
507
+ const listLen = curves.length * 2;
508
+ const buf = Buffer.allocUnsafe(2 + listLen);
509
+ buf.writeUInt16BE(listLen, 0);
510
+ for (let i = 0; i < curves.length; i++) {
511
+ buf.writeUInt16BE(curves[i]!, 2 + i * 2);
512
+ }
513
+ return buf;
514
+ }
515
+
516
+ /**
517
+ * Build signature_algorithms extension
518
+ */
519
+ export function buildSignatureAlgorithmsExtension(
520
+ pairs: Array<{ hash: number; sig: number }>,
521
+ ): Buffer {
522
+ const listLen = pairs.length * 2;
523
+ const buf = Buffer.allocUnsafe(2 + listLen);
524
+ buf.writeUInt16BE(listLen, 0);
525
+ for (let i = 0; i < pairs.length; i++) {
526
+ buf.writeUInt8(pairs[i]!.hash, 2 + i * 2);
527
+ buf.writeUInt8(pairs[i]!.sig, 2 + i * 2 + 1);
528
+ }
529
+ return buf;
530
+ }
531
+
532
+ // ─── Utility ──────────────────────────────────────────────────────────────────
533
+
534
+ function writeUInt24BE(buf: Buffer, offset: number, value: number): void {
535
+ buf.writeUInt8((value >> 16) & 0xff, offset);
536
+ buf.writeUInt8((value >> 8) & 0xff, offset + 1);
537
+ buf.writeUInt8(value & 0xff, offset + 2);
538
+ }
539
+
540
+ function readUInt24BE(buf: Buffer, offset: number): number {
541
+ return (buf.readUInt8(offset) << 16) | (buf.readUInt8(offset + 1) << 8) | buf.readUInt8(offset + 2);
542
+ }
543
+
544
+ export { DTLS_VERSION_1_2 };
package/src/index.ts ADDED
@@ -0,0 +1,72 @@
1
+ // @agentdance/node-webrtc-dtls – DTLS 1.2 implementation
2
+ // Public API
3
+
4
+ export { ContentType, DTLS_VERSION_1_2, type DtlsRecord, type DtlsVersion } from './types.js';
5
+ export {
6
+ encodeRecord,
7
+ decodeRecords,
8
+ isDtlsPacket,
9
+ makeRecord,
10
+ } from './record.js';
11
+ export {
12
+ HandshakeType,
13
+ CipherSuites,
14
+ ExtensionType,
15
+ NamedCurve,
16
+ SrtpProtectionProfile,
17
+ type HandshakeMessage,
18
+ type ClientHello,
19
+ type ServerHello,
20
+ type HelloVerifyRequest,
21
+ type ServerKeyExchange,
22
+ type ClientKeyExchange,
23
+ type TlsExtension,
24
+ encodeHandshakeMessage,
25
+ decodeHandshakeMessage,
26
+ encodeClientHello,
27
+ decodeClientHello,
28
+ encodeServerHello,
29
+ decodeServerHello,
30
+ encodeHelloVerifyRequest,
31
+ decodeHelloVerifyRequest,
32
+ encodeCertificate,
33
+ decodeCertificate,
34
+ encodeServerKeyExchange,
35
+ decodeServerKeyExchange,
36
+ encodeClientKeyExchange,
37
+ decodeClientKeyExchange,
38
+ buildUseSrtpExtension,
39
+ buildSupportedGroupsExtension,
40
+ } from './handshake.js';
41
+ export {
42
+ prf,
43
+ hmacSha256,
44
+ computeMasterSecret,
45
+ expandKeyMaterial,
46
+ exportKeyingMaterial,
47
+ aesgcmEncrypt,
48
+ aesgcmDecrypt,
49
+ generateEcdhKeyPair,
50
+ computeEcdhPreMasterSecret,
51
+ encodeEcPublicKey,
52
+ decodeEcPublicKey,
53
+ ecdsaSign,
54
+ ecdsaVerify,
55
+ sha256,
56
+ type AesGcmResult,
57
+ type EcdhKeyPair,
58
+ type KeyBlock,
59
+ } from './crypto.js';
60
+ export {
61
+ type DtlsCertificate,
62
+ generateSelfSignedCertificate,
63
+ computeFingerprint,
64
+ verifyFingerprint,
65
+ extractPublicKeyFromCert,
66
+ } from './certificate.js';
67
+ export { DtlsState, type SecurityParameters, type HandshakeContext, type CipherState } from './state.js';
68
+ export {
69
+ DtlsTransport,
70
+ type DtlsTransportOptions,
71
+ type SrtpKeyingMaterial,
72
+ } from './transport.js';