@agentdance/node-webrtc-srtp 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.
- package/dist/auth.d.ts +27 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +46 -0
- package/dist/auth.js.map +1 -0
- package/dist/cipher.d.ts +24 -0
- package/dist/cipher.d.ts.map +1 -0
- package/dist/cipher.js +61 -0
- package/dist/cipher.js.map +1 -0
- package/dist/context.d.ts +30 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +104 -0
- package/dist/context.js.map +1 -0
- package/dist/gcm.d.ts +14 -0
- package/dist/gcm.d.ts.map +1 -0
- package/dist/gcm.js +151 -0
- package/dist/gcm.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/protect.d.ts +21 -0
- package/dist/protect.d.ts.map +1 -0
- package/dist/protect.js +129 -0
- package/dist/protect.js.map +1 -0
- package/dist/replay.d.ts +22 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +56 -0
- package/dist/replay.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/unprotect.d.ts +14 -0
- package/dist/unprotect.d.ts.map +1 -0
- package/dist/unprotect.js +156 -0
- package/dist/unprotect.js.map +1 -0
- package/package.json +57 -0
- package/src/auth.ts +61 -0
- package/src/cipher.ts +68 -0
- package/src/context.ts +130 -0
- package/src/gcm.ts +174 -0
- package/src/index.ts +18 -0
- package/src/protect.ts +160 -0
- package/src/replay.ts +57 -0
- package/src/types.ts +55 -0
- package/src/unprotect.ts +174 -0
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the SRTP authentication tag.
|
|
3
|
+
*
|
|
4
|
+
* auth_tag = first `tagLength` bytes of
|
|
5
|
+
* HMAC-SHA1(k_a, RTP_header || RTP_payload || ROC_be32)
|
|
6
|
+
*
|
|
7
|
+
* @param authKey 20-byte HMAC-SHA1 session authentication key
|
|
8
|
+
* @param rtpHeader The full RTP header (variable length)
|
|
9
|
+
* @param rtpPayload The plain RTP payload (pre-encryption for sender,
|
|
10
|
+
* already encrypted on sender side per RFC 3711 §3.1)
|
|
11
|
+
* @param roc Roll-Over Counter (big-endian 32-bit appended)
|
|
12
|
+
* @param tagLength 10 for 80-bit profile, 4 for 32-bit profile
|
|
13
|
+
*/
|
|
14
|
+
export declare function computeSrtpAuthTag(authKey: Buffer, rtpHeader: Buffer, rtpPayload: Buffer, roc: number, tagLength: 10 | 4): Buffer;
|
|
15
|
+
/**
|
|
16
|
+
* Compute the SRTCP authentication tag.
|
|
17
|
+
*
|
|
18
|
+
* auth_tag = first `tagLength` bytes of
|
|
19
|
+
* HMAC-SHA1(k_a, RTCP_packet || E_SRTCP_index_be32)
|
|
20
|
+
*
|
|
21
|
+
* @param authKey 20-byte HMAC-SHA1 session authentication key
|
|
22
|
+
* @param rtcpPacket The full (partially encrypted) RTCP packet bytes
|
|
23
|
+
* @param eSrtcpIndex The 32-bit word: E(1) || SRTCP_index(31)
|
|
24
|
+
* @param tagLength 10 for 80-bit profile, 4 for 32-bit profile
|
|
25
|
+
*/
|
|
26
|
+
export declare function computeSrtcpAuthTag(authKey: Buffer, rtcpPacket: Buffer, eSrtcpIndex: number, tagLength: 10 | 4): Buffer;
|
|
27
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,EAAE,GAAG,CAAC,GAChB,MAAM,CASR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,EAAE,GAAG,CAAC,GAChB,MAAM,CAQR"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createHmac } from 'node:crypto';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// HMAC-SHA1 authentication tags (RFC 3711 §4.2)
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
/**
|
|
6
|
+
* Compute the SRTP authentication tag.
|
|
7
|
+
*
|
|
8
|
+
* auth_tag = first `tagLength` bytes of
|
|
9
|
+
* HMAC-SHA1(k_a, RTP_header || RTP_payload || ROC_be32)
|
|
10
|
+
*
|
|
11
|
+
* @param authKey 20-byte HMAC-SHA1 session authentication key
|
|
12
|
+
* @param rtpHeader The full RTP header (variable length)
|
|
13
|
+
* @param rtpPayload The plain RTP payload (pre-encryption for sender,
|
|
14
|
+
* already encrypted on sender side per RFC 3711 §3.1)
|
|
15
|
+
* @param roc Roll-Over Counter (big-endian 32-bit appended)
|
|
16
|
+
* @param tagLength 10 for 80-bit profile, 4 for 32-bit profile
|
|
17
|
+
*/
|
|
18
|
+
export function computeSrtpAuthTag(authKey, rtpHeader, rtpPayload, roc, tagLength) {
|
|
19
|
+
const rocBuf = Buffer.allocUnsafe(4);
|
|
20
|
+
rocBuf.writeUInt32BE(roc >>> 0, 0);
|
|
21
|
+
const hmac = createHmac('sha1', authKey);
|
|
22
|
+
hmac.update(rtpHeader);
|
|
23
|
+
hmac.update(rtpPayload);
|
|
24
|
+
hmac.update(rocBuf);
|
|
25
|
+
return hmac.digest().subarray(0, tagLength);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Compute the SRTCP authentication tag.
|
|
29
|
+
*
|
|
30
|
+
* auth_tag = first `tagLength` bytes of
|
|
31
|
+
* HMAC-SHA1(k_a, RTCP_packet || E_SRTCP_index_be32)
|
|
32
|
+
*
|
|
33
|
+
* @param authKey 20-byte HMAC-SHA1 session authentication key
|
|
34
|
+
* @param rtcpPacket The full (partially encrypted) RTCP packet bytes
|
|
35
|
+
* @param eSrtcpIndex The 32-bit word: E(1) || SRTCP_index(31)
|
|
36
|
+
* @param tagLength 10 for 80-bit profile, 4 for 32-bit profile
|
|
37
|
+
*/
|
|
38
|
+
export function computeSrtcpAuthTag(authKey, rtcpPacket, eSrtcpIndex, tagLength) {
|
|
39
|
+
const indexBuf = Buffer.allocUnsafe(4);
|
|
40
|
+
indexBuf.writeUInt32BE(eSrtcpIndex >>> 0, 0);
|
|
41
|
+
const hmac = createHmac('sha1', authKey);
|
|
42
|
+
hmac.update(rtcpPacket);
|
|
43
|
+
hmac.update(indexBuf);
|
|
44
|
+
return hmac.digest().subarray(0, tagLength);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,8EAA8E;AAC9E,iDAAiD;AACjD,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAe,EACf,SAAiB,EACjB,UAAkB,EAClB,GAAW,EACX,SAAiB;IAEjB,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAEnC,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,UAAkB,EAClB,WAAmB,EACnB,SAAiB;IAEjB,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACvC,QAAQ,CAAC,aAAa,CAAC,WAAW,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7C,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/cipher.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate `length` bytes of AES-128-CM (Counter Mode) keystream.
|
|
3
|
+
*
|
|
4
|
+
* @param key 16-byte AES key
|
|
5
|
+
* @param iv 16-byte IV (the initial counter block, lower 16 bits = 0)
|
|
6
|
+
* @param length Number of keystream bytes to return
|
|
7
|
+
*/
|
|
8
|
+
export declare function aes128cmKeystream(key: Buffer, iv: Buffer, length: number): Buffer;
|
|
9
|
+
/**
|
|
10
|
+
* Compute the 128-bit SRTP IV:
|
|
11
|
+
* IV = (k_s * 2^16) XOR (SSRC * 2^64) XOR (index * 2^16)
|
|
12
|
+
*
|
|
13
|
+
* Bit-position reference (big-endian 128-bit / 16-byte buffer):
|
|
14
|
+
* k_s * 2^16 → salt (14 bytes) placed at buffer bytes [2..15]
|
|
15
|
+
* SSRC * 2^64 → SSRC (4 bytes) placed at buffer bytes [4..7]
|
|
16
|
+
* i * 2^16 → index (6 bytes) placed at buffer bytes [8..13]
|
|
17
|
+
*/
|
|
18
|
+
export declare function computeSrtpIv(salt: Buffer, ssrc: number, index: bigint): Buffer;
|
|
19
|
+
/**
|
|
20
|
+
* Compute the 128-bit SRTCP IV.
|
|
21
|
+
* Identical formula to SRTP; the SRTCP index is 31-bit.
|
|
22
|
+
*/
|
|
23
|
+
export declare function computeSrtcpIv(salt: Buffer, ssrc: number, index: number): Buffer;
|
|
24
|
+
//# sourceMappingURL=cipher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cipher.d.ts","sourceRoot":"","sources":["../src/cipher.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CASjF;AAMD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAkB/E;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF"}
|
package/dist/cipher.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createCipheriv } from 'node:crypto';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// AES-128-CM keystream (RFC 3711 §4.1.1)
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
/**
|
|
6
|
+
* Generate `length` bytes of AES-128-CM (Counter Mode) keystream.
|
|
7
|
+
*
|
|
8
|
+
* @param key 16-byte AES key
|
|
9
|
+
* @param iv 16-byte IV (the initial counter block, lower 16 bits = 0)
|
|
10
|
+
* @param length Number of keystream bytes to return
|
|
11
|
+
*/
|
|
12
|
+
export function aes128cmKeystream(key, iv, length) {
|
|
13
|
+
if (key.length !== 16)
|
|
14
|
+
throw new RangeError('AES-128-CM requires a 16-byte key');
|
|
15
|
+
if (iv.length !== 16)
|
|
16
|
+
throw new RangeError('AES-128-CM IV must be 16 bytes');
|
|
17
|
+
// Encrypt a zero plaintext; CTR mode emits the keystream directly.
|
|
18
|
+
const plaintext = Buffer.alloc(length, 0);
|
|
19
|
+
const cipher = createCipheriv('aes-128-ctr', key, iv);
|
|
20
|
+
const ks = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
21
|
+
return ks.subarray(0, length);
|
|
22
|
+
}
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// IV computation for SRTP (RFC 3711 §4.1)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Compute the 128-bit SRTP IV:
|
|
28
|
+
* IV = (k_s * 2^16) XOR (SSRC * 2^64) XOR (index * 2^16)
|
|
29
|
+
*
|
|
30
|
+
* Bit-position reference (big-endian 128-bit / 16-byte buffer):
|
|
31
|
+
* k_s * 2^16 → salt (14 bytes) placed at buffer bytes [2..15]
|
|
32
|
+
* SSRC * 2^64 → SSRC (4 bytes) placed at buffer bytes [4..7]
|
|
33
|
+
* i * 2^16 → index (6 bytes) placed at buffer bytes [8..13]
|
|
34
|
+
*/
|
|
35
|
+
export function computeSrtpIv(salt, ssrc, index) {
|
|
36
|
+
if (salt.length !== 14)
|
|
37
|
+
throw new RangeError('SRTP salt must be 14 bytes');
|
|
38
|
+
// Start from all zeros; copy salt into bytes [2..15].
|
|
39
|
+
const iv = Buffer.alloc(16, 0);
|
|
40
|
+
salt.copy(iv, 2);
|
|
41
|
+
// XOR SSRC into bytes [4..7].
|
|
42
|
+
iv.writeUInt32BE((iv.readUInt32BE(4) ^ (ssrc >>> 0)) >>> 0, 4);
|
|
43
|
+
// XOR 48-bit index into bytes [8..13].
|
|
44
|
+
// index high 32 bits → bytes [8..11], index low 16 bits → bytes [12..13].
|
|
45
|
+
const idxHi = Number((index >> 16n) & 0xffffffffn);
|
|
46
|
+
const idxLo = Number(index & 0xffffn);
|
|
47
|
+
iv.writeUInt32BE((iv.readUInt32BE(8) ^ idxHi) >>> 0, 8);
|
|
48
|
+
iv.writeUInt16BE((iv.readUInt16BE(12) ^ idxLo) & 0xffff, 12);
|
|
49
|
+
return iv;
|
|
50
|
+
}
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// IV computation for SRTCP (RFC 3711 §4.1)
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
/**
|
|
55
|
+
* Compute the 128-bit SRTCP IV.
|
|
56
|
+
* Identical formula to SRTP; the SRTCP index is 31-bit.
|
|
57
|
+
*/
|
|
58
|
+
export function computeSrtcpIv(salt, ssrc, index) {
|
|
59
|
+
return computeSrtpIv(salt, ssrc, BigInt(index));
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=cipher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cipher.js","sourceRoot":"","sources":["../src/cipher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,EAAU,EAAE,MAAc;IACvE,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,mCAAmC,CAAC,CAAC;IACjF,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,gCAAgC,CAAC,CAAC;IAE7E,mEAAmE;IACnE,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,2CAA2C;AAC3C,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,IAAY,EAAE,KAAa;IACrE,IAAI,IAAI,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,CAAC;IAE3E,sDAAsD;IACtD,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAEjB,8BAA8B;IAC9B,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IAE/D,uCAAuC;IACvC,0EAA0E;IAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC;IACtC,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC,CAAC;IAE7D,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,4CAA4C;AAC5C,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAY,EAAE,KAAa;IACtE,OAAO,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SrtpKeyingMaterial, SrtpContext, SrtcpContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Derive a session key using the AES-CM key derivation function.
|
|
4
|
+
*
|
|
5
|
+
* The PRF key is the master key; the PRF input (IV) is:
|
|
6
|
+
* PRF_input = master_salt XOR (label * 2^48) XOR (r * 2^???)
|
|
7
|
+
*
|
|
8
|
+
* For KDR=0 (the common case), r=0 and the second XOR term vanishes.
|
|
9
|
+
* The label is placed at byte offset 7 in the 14-byte salt (bit position 48).
|
|
10
|
+
*
|
|
11
|
+
* RFC 3711 §4.3.1:
|
|
12
|
+
* x = label * 2^48 (label occupies bits 48-55 of the 112-bit salt field)
|
|
13
|
+
*
|
|
14
|
+
* @param masterKey 16-byte AES master key
|
|
15
|
+
* @param masterSalt 14-byte master salt
|
|
16
|
+
* @param label 0x00=enc, 0x01=auth, 0x02=salt
|
|
17
|
+
* @param length Desired output length in bytes
|
|
18
|
+
* @param r Key derivation rate index (default 0)
|
|
19
|
+
*/
|
|
20
|
+
export declare function deriveSessionKey(masterKey: Buffer, masterSalt: Buffer, label: number, length: number, r?: bigint): Buffer;
|
|
21
|
+
/**
|
|
22
|
+
* Derive all three SRTP session keys from the supplied master keying material
|
|
23
|
+
* and return an initialised SrtpContext.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createSrtpContext(material: SrtpKeyingMaterial): SrtpContext;
|
|
26
|
+
/**
|
|
27
|
+
* Derive all three SRTCP session keys and return an initialised SrtcpContext.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createSrtcpContext(material: SrtpKeyingMaterial): SrtcpContext;
|
|
30
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,kBAAkB,EAClB,WAAW,EACX,YAAY,EACb,MAAM,YAAY,CAAC;AAQpB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,CAAC,GAAE,MAAW,GACb,MAAM,CAgCR;AAcD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,WAAW,CAmB3E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,YAAY,CAiB7E"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ProtectionProfile, } from './types.js';
|
|
2
|
+
import { aes128cmKeystream } from './cipher.js';
|
|
3
|
+
import { ReplayWindow } from './replay.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// AES-128-CM PRF (RFC 3711 §4.3.1)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/**
|
|
8
|
+
* Derive a session key using the AES-CM key derivation function.
|
|
9
|
+
*
|
|
10
|
+
* The PRF key is the master key; the PRF input (IV) is:
|
|
11
|
+
* PRF_input = master_salt XOR (label * 2^48) XOR (r * 2^???)
|
|
12
|
+
*
|
|
13
|
+
* For KDR=0 (the common case), r=0 and the second XOR term vanishes.
|
|
14
|
+
* The label is placed at byte offset 7 in the 14-byte salt (bit position 48).
|
|
15
|
+
*
|
|
16
|
+
* RFC 3711 §4.3.1:
|
|
17
|
+
* x = label * 2^48 (label occupies bits 48-55 of the 112-bit salt field)
|
|
18
|
+
*
|
|
19
|
+
* @param masterKey 16-byte AES master key
|
|
20
|
+
* @param masterSalt 14-byte master salt
|
|
21
|
+
* @param label 0x00=enc, 0x01=auth, 0x02=salt
|
|
22
|
+
* @param length Desired output length in bytes
|
|
23
|
+
* @param r Key derivation rate index (default 0)
|
|
24
|
+
*/
|
|
25
|
+
export function deriveSessionKey(masterKey, masterSalt, label, length, r = 0n) {
|
|
26
|
+
if (masterSalt.length !== 14)
|
|
27
|
+
throw new RangeError('masterSalt must be 14 bytes');
|
|
28
|
+
// Build the 112-bit (14-byte) x value, then zero-pad to 16 bytes (right-pad
|
|
29
|
+
// with two 0x00 bytes) to form the IV for AES-CM.
|
|
30
|
+
//
|
|
31
|
+
// x = master_salt XOR (label << 48) XOR (r << kdr_shift)
|
|
32
|
+
// For KDR = 0 the r term is zero.
|
|
33
|
+
//
|
|
34
|
+
// label << 48 places the label byte at offset 6 (0-indexed) in the 14-byte
|
|
35
|
+
// big-endian integer.
|
|
36
|
+
const x = Buffer.from(masterSalt);
|
|
37
|
+
// XOR in label at byte offset 7 (label is at bit position 48 of the
|
|
38
|
+
// 112-bit big-endian value, i.e. byte index 7 counting from the MSB).
|
|
39
|
+
x.writeUInt8(x.readUInt8(7) ^ (label & 0xff), 7);
|
|
40
|
+
// If r != 0 we XOR it into the low 8 bytes of x (bytes 6..13).
|
|
41
|
+
// KDR=0 is the common case so this branch is rarely taken.
|
|
42
|
+
if (r !== 0n) {
|
|
43
|
+
for (let i = 0; i < 8; i++) {
|
|
44
|
+
const shift = BigInt((7 - i) * 8);
|
|
45
|
+
const off = 6 + i;
|
|
46
|
+
x.writeUInt8(x.readUInt8(off) ^ Number((r >> shift) & 0xffn), off);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Zero-pad to 16 bytes (append two 0x00 bytes) → this is the AES-CM IV
|
|
50
|
+
const iv = Buffer.alloc(16, 0);
|
|
51
|
+
x.copy(iv, 0); // x occupies bytes 0..13; bytes 14,15 remain 0
|
|
52
|
+
return aes128cmKeystream(masterKey, iv, length);
|
|
53
|
+
}
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Tag length helpers
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
function authTagLength(profile) {
|
|
58
|
+
return profile === ProtectionProfile.AES_128_CM_HMAC_SHA1_32 ? 4 : 10;
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Context factories
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
/**
|
|
64
|
+
* Derive all three SRTP session keys from the supplied master keying material
|
|
65
|
+
* and return an initialised SrtpContext.
|
|
66
|
+
*/
|
|
67
|
+
export function createSrtpContext(material) {
|
|
68
|
+
const { masterKey, masterSalt, profile } = material;
|
|
69
|
+
const sessionEncKey = deriveSessionKey(masterKey, masterSalt, 0x00, 16);
|
|
70
|
+
const sessionSaltKey = deriveSessionKey(masterKey, masterSalt, 0x02, 14);
|
|
71
|
+
// Auth key is only meaningful for CM profiles; for GCM we still derive it
|
|
72
|
+
// (harmless) but it won't be used for packet authentication.
|
|
73
|
+
const sessionAuthKey = deriveSessionKey(masterKey, masterSalt, 0x01, 20);
|
|
74
|
+
return {
|
|
75
|
+
profile,
|
|
76
|
+
sessionEncKey,
|
|
77
|
+
sessionAuthKey,
|
|
78
|
+
sessionSaltKey,
|
|
79
|
+
index: 0n,
|
|
80
|
+
rolloverCounter: 0,
|
|
81
|
+
lastSeq: -1,
|
|
82
|
+
replayWindow: new ReplayWindow(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Derive all three SRTCP session keys and return an initialised SrtcpContext.
|
|
87
|
+
*/
|
|
88
|
+
export function createSrtcpContext(material) {
|
|
89
|
+
const { masterKey, masterSalt, profile } = material;
|
|
90
|
+
// SRTCP uses separate labels (same label values, different contexts per
|
|
91
|
+
// spec; in practice the same KDF is used with the same master key/salt).
|
|
92
|
+
const sessionEncKey = deriveSessionKey(masterKey, masterSalt, 0x00, 16);
|
|
93
|
+
const sessionSaltKey = deriveSessionKey(masterKey, masterSalt, 0x02, 14);
|
|
94
|
+
const sessionAuthKey = deriveSessionKey(masterKey, masterSalt, 0x01, 20);
|
|
95
|
+
return {
|
|
96
|
+
profile,
|
|
97
|
+
sessionEncKey,
|
|
98
|
+
sessionAuthKey,
|
|
99
|
+
sessionSaltKey,
|
|
100
|
+
index: 0,
|
|
101
|
+
replayWindow: new ReplayWindow(),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,GAIlB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,8EAA8E;AAC9E,oCAAoC;AACpC,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,UAAkB,EAClB,KAAa,EACb,MAAc,EACd,IAAY,EAAE;IAEd,IAAI,UAAU,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,6BAA6B,CAAC,CAAC;IAElF,4EAA4E;IAC5E,kDAAkD;IAClD,EAAE;IACF,yDAAyD;IACzD,kCAAkC;IAClC,EAAE;IACF,2EAA2E;IAC3E,sBAAsB;IACtB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAElC,oEAAoE;IACpE,sEAAsE;IACtE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAEjD,+DAA+D;IAC/D,2DAA2D;IAC3D,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;QACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAClC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;QACrE,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,+CAA+C;IAE9D,OAAO,iBAAiB,CAAC,SAAS,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E,SAAS,aAAa,CAAC,OAA0B;IAC/C,OAAO,OAAO,KAAK,iBAAiB,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAA4B;IAC5D,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IAEpD,MAAM,aAAa,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACxE,MAAM,cAAc,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACzE,0EAA0E;IAC1E,6DAA6D;IAC7D,MAAM,cAAc,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAEzE,OAAO;QACL,OAAO;QACP,aAAa;QACb,cAAc;QACd,cAAc;QACd,KAAK,EAAE,EAAE;QACT,eAAe,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC,CAAC;QACX,YAAY,EAAE,IAAI,YAAY,EAAE;KACjC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAA4B;IAC7D,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC;IAEpD,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,aAAa,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACxE,MAAM,cAAc,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IACzE,MAAM,cAAc,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;IAEzE,OAAO;QACL,OAAO;QACP,aAAa;QACb,cAAc;QACd,cAAc;QACd,KAAK,EAAE,CAAC;QACR,YAAY,EAAE,IAAI,YAAY,EAAE;KACjC,CAAC;AACJ,CAAC"}
|
package/dist/gcm.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SrtpContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Encrypt and authenticate an SRTP packet using AES-128-GCM.
|
|
4
|
+
*
|
|
5
|
+
* Packet layout after protection:
|
|
6
|
+
* RTP Header (AAD, unchanged) | Encrypted Payload | GCM Auth Tag (16 bytes)
|
|
7
|
+
*/
|
|
8
|
+
export declare function gcmSrtpProtect(ctx: SrtpContext, rtpPacket: Buffer): Buffer;
|
|
9
|
+
/**
|
|
10
|
+
* Authenticate and decrypt a GCM-protected SRTP packet.
|
|
11
|
+
* Returns null if authentication fails or replay is detected.
|
|
12
|
+
*/
|
|
13
|
+
export declare function gcmSrtpUnprotect(ctx: SrtpContext, srtpPacket: Buffer): Buffer | null;
|
|
14
|
+
//# sourceMappingURL=gcm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcm.d.ts","sourceRoot":"","sources":["../src/gcm.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAsEzC;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAsB1E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+BpF"}
|
package/dist/gcm.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv } from 'node:crypto';
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// AES-128-GCM (RFC 7714)
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// GCM auth tag is always 16 bytes (128-bit).
|
|
6
|
+
const GCM_TAG_LENGTH = 16;
|
|
7
|
+
// GCM nonce/IV is 12 bytes.
|
|
8
|
+
const GCM_IV_LENGTH = 12;
|
|
9
|
+
/**
|
|
10
|
+
* Build the 12-byte GCM IV from the 12-byte salt, SSRC, and packet index.
|
|
11
|
+
*
|
|
12
|
+
* RFC 7714 §8.1:
|
|
13
|
+
* IV = salt XOR (SSRC placed at bytes 4..7) XOR (index placed at bytes 4..11)
|
|
14
|
+
*
|
|
15
|
+
* More precisely for SRTP (48-bit index):
|
|
16
|
+
* - Bytes 0..3: salt[0..3] XOR 0 XOR 0 (no SSRC/index contribution)
|
|
17
|
+
* - Bytes 4..7: salt[4..7] XOR SSRC_be32 XOR index_hi32
|
|
18
|
+
* - Bytes 8..11: salt[8..11] XOR 0 XOR index_lo32
|
|
19
|
+
*
|
|
20
|
+
* Wait – GCM salt is typically 12 bytes, not 14. When used inside the
|
|
21
|
+
* standard AES-CM derivation pipeline the 14-byte session salt is trimmed
|
|
22
|
+
* to 12 bytes by dropping the two trailing zero bytes (they were zero-padded
|
|
23
|
+
* anyway in the PRF output, but in practice the caller passes the full 14).
|
|
24
|
+
* We take the first 12 bytes of the supplied salt.
|
|
25
|
+
*/
|
|
26
|
+
function buildGcmIv(salt, ssrc, index) {
|
|
27
|
+
// Use first 12 bytes; remaining 2 are the PRF zero-padding.
|
|
28
|
+
const iv = Buffer.alloc(GCM_IV_LENGTH, 0);
|
|
29
|
+
salt.copy(iv, 0, 0, GCM_IV_LENGTH);
|
|
30
|
+
// XOR SSRC into bytes 4..7
|
|
31
|
+
iv.writeUInt32BE(iv.readUInt32BE(4) ^ (ssrc >>> 0), 4);
|
|
32
|
+
// XOR 48-bit index (big-endian 6 bytes) into bytes 6..11.
|
|
33
|
+
// Store index into a temporary 8-byte buffer; bytes [2..7] hold the 48-bit value.
|
|
34
|
+
const idxBytes = Buffer.allocUnsafe(8);
|
|
35
|
+
idxBytes.writeBigUInt64BE(index & 0xffffffffffffn, 0);
|
|
36
|
+
// bytes 6..7 overlap with the SSRC field – XOR as a 16-bit word
|
|
37
|
+
iv.writeUInt16BE(iv.readUInt16BE(6) ^ idxBytes.readUInt16BE(2), 6);
|
|
38
|
+
// bytes 8..11 – XOR as a 32-bit word
|
|
39
|
+
iv.writeUInt32BE(iv.readUInt32BE(8) ^ idxBytes.readUInt32BE(4), 8);
|
|
40
|
+
return iv;
|
|
41
|
+
}
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Parse minimal RTP header to extract SSRC and payload offset
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
function parseRtpHeader(packet) {
|
|
46
|
+
if (packet.length < 12)
|
|
47
|
+
throw new RangeError('RTP packet too short');
|
|
48
|
+
const cc = packet[0] & 0x0f;
|
|
49
|
+
const x = (packet[0] & 0x10) !== 0;
|
|
50
|
+
let headerLen = 12 + cc * 4;
|
|
51
|
+
if (x) {
|
|
52
|
+
if (packet.length < headerLen + 4)
|
|
53
|
+
throw new RangeError('RTP header extension truncated');
|
|
54
|
+
const extLen = packet.readUInt16BE(headerLen + 2);
|
|
55
|
+
headerLen += 4 + extLen * 4;
|
|
56
|
+
}
|
|
57
|
+
const ssrc = packet.readUInt32BE(8);
|
|
58
|
+
return { ssrc, headerLen };
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// GCM protect / unprotect
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
/**
|
|
64
|
+
* Encrypt and authenticate an SRTP packet using AES-128-GCM.
|
|
65
|
+
*
|
|
66
|
+
* Packet layout after protection:
|
|
67
|
+
* RTP Header (AAD, unchanged) | Encrypted Payload | GCM Auth Tag (16 bytes)
|
|
68
|
+
*/
|
|
69
|
+
export function gcmSrtpProtect(ctx, rtpPacket) {
|
|
70
|
+
const { ssrc, headerLen } = parseRtpHeader(rtpPacket);
|
|
71
|
+
const seq = rtpPacket.readUInt16BE(2);
|
|
72
|
+
// Compute packet index and update context state.
|
|
73
|
+
const index = computeIndex(ctx, seq);
|
|
74
|
+
const iv = buildGcmIv(ctx.sessionSaltKey, ssrc, index);
|
|
75
|
+
const header = rtpPacket.subarray(0, headerLen);
|
|
76
|
+
const payload = rtpPacket.subarray(headerLen);
|
|
77
|
+
const cipher = createCipheriv('aes-128-gcm', ctx.sessionEncKey, iv);
|
|
78
|
+
cipher.setAAD(header);
|
|
79
|
+
const encrypted = Buffer.concat([cipher.update(payload), cipher.final()]);
|
|
80
|
+
const tag = cipher.getAuthTag();
|
|
81
|
+
// Update context
|
|
82
|
+
ctx.index = index;
|
|
83
|
+
ctx.rolloverCounter = Number(index >> 16n) >>> 0;
|
|
84
|
+
ctx.lastSeq = seq;
|
|
85
|
+
return Buffer.concat([header, encrypted, tag]);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Authenticate and decrypt a GCM-protected SRTP packet.
|
|
89
|
+
* Returns null if authentication fails or replay is detected.
|
|
90
|
+
*/
|
|
91
|
+
export function gcmSrtpUnprotect(ctx, srtpPacket) {
|
|
92
|
+
if (srtpPacket.length < 12 + GCM_TAG_LENGTH)
|
|
93
|
+
return null;
|
|
94
|
+
const { ssrc, headerLen } = parseRtpHeader(srtpPacket);
|
|
95
|
+
const seq = srtpPacket.readUInt16BE(2);
|
|
96
|
+
const index = estimateIndex(ctx, seq);
|
|
97
|
+
if (!ctx.replayWindow.check(index))
|
|
98
|
+
return null;
|
|
99
|
+
const header = srtpPacket.subarray(0, headerLen);
|
|
100
|
+
const encryptedPayload = srtpPacket.subarray(headerLen, srtpPacket.length - GCM_TAG_LENGTH);
|
|
101
|
+
const tag = srtpPacket.subarray(srtpPacket.length - GCM_TAG_LENGTH);
|
|
102
|
+
const iv = buildGcmIv(ctx.sessionSaltKey, ssrc, index);
|
|
103
|
+
try {
|
|
104
|
+
const decipher = createDecipheriv('aes-128-gcm', ctx.sessionEncKey, iv);
|
|
105
|
+
decipher.setAAD(header);
|
|
106
|
+
decipher.setAuthTag(tag);
|
|
107
|
+
const decrypted = Buffer.concat([decipher.update(encryptedPayload), decipher.final()]);
|
|
108
|
+
ctx.replayWindow.update(index);
|
|
109
|
+
ctx.index = index;
|
|
110
|
+
ctx.rolloverCounter = Number(index >> 16n) >>> 0;
|
|
111
|
+
ctx.lastSeq = seq;
|
|
112
|
+
return Buffer.concat([header, decrypted]);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Index helpers (shared logic – duplicated from protect/unprotect to keep
|
|
120
|
+
// gcm.ts self-contained; in a real project you'd factor these out)
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
function computeIndex(ctx, seq) {
|
|
123
|
+
if (ctx.lastSeq === -1) {
|
|
124
|
+
return BigInt(ctx.rolloverCounter) << 16n | BigInt(seq);
|
|
125
|
+
}
|
|
126
|
+
const roc = BigInt(ctx.rolloverCounter);
|
|
127
|
+
const lastSeq = ctx.lastSeq;
|
|
128
|
+
// If seq wraps forward (65535 → 0), increment ROC
|
|
129
|
+
if (seq < lastSeq && lastSeq - seq > 0x8000) {
|
|
130
|
+
return (roc + 1n) << 16n | BigInt(seq);
|
|
131
|
+
}
|
|
132
|
+
return roc << 16n | BigInt(seq);
|
|
133
|
+
}
|
|
134
|
+
function estimateIndex(ctx, seq) {
|
|
135
|
+
if (ctx.lastSeq === -1) {
|
|
136
|
+
return BigInt(ctx.rolloverCounter) << 16n | BigInt(seq);
|
|
137
|
+
}
|
|
138
|
+
// RFC 3711 §3.3.1 index estimation
|
|
139
|
+
const v = BigInt(ctx.rolloverCounter);
|
|
140
|
+
const diff = seq - ctx.lastSeq;
|
|
141
|
+
if (diff > 0x8000) {
|
|
142
|
+
// seq is much less than lastSeq → different ROC (wrap back?)
|
|
143
|
+
return (v === 0n ? 0n : v - 1n) << 16n | BigInt(seq);
|
|
144
|
+
}
|
|
145
|
+
else if (diff < -0x8000) {
|
|
146
|
+
// seq is much greater → ROC incremented
|
|
147
|
+
return (v + 1n) << 16n | BigInt(seq);
|
|
148
|
+
}
|
|
149
|
+
return v << 16n | BigInt(seq);
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=gcm.js.map
|
package/dist/gcm.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcm.js","sourceRoot":"","sources":["../src/gcm.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ProtectionProfile } from './types.js';
|
|
2
|
+
export type { SrtpKeyingMaterial, SrtpContext, SrtcpContext } from './types.js';
|
|
3
|
+
export { createSrtpContext, createSrtcpContext, deriveSessionKey } from './context.js';
|
|
4
|
+
export { aes128cmKeystream, computeSrtpIv, computeSrtcpIv } from './cipher.js';
|
|
5
|
+
export { computeSrtpAuthTag, computeSrtcpAuthTag } from './auth.js';
|
|
6
|
+
export { gcmSrtpProtect, gcmSrtpUnprotect } from './gcm.js';
|
|
7
|
+
export { ReplayWindow } from './replay.js';
|
|
8
|
+
export { srtpProtect, srtcpProtect } from './protect.js';
|
|
9
|
+
export { srtpUnprotect, srtcpUnprotect } from './unprotect.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhF,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Public API – re-export everything from the srtp package.
|
|
2
|
+
export { ProtectionProfile } from './types.js';
|
|
3
|
+
export { createSrtpContext, createSrtcpContext, deriveSessionKey } from './context.js';
|
|
4
|
+
export { aes128cmKeystream, computeSrtpIv, computeSrtcpIv } from './cipher.js';
|
|
5
|
+
export { computeSrtpAuthTag, computeSrtcpAuthTag } from './auth.js';
|
|
6
|
+
export { gcmSrtpProtect, gcmSrtpUnprotect } from './gcm.js';
|
|
7
|
+
export { ReplayWindow } from './replay.js';
|
|
8
|
+
export { srtpProtect, srtcpProtect } from './protect.js';
|
|
9
|
+
export { srtpUnprotect, srtcpUnprotect } from './unprotect.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAE3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEvF,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SrtpContext, SrtcpContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Protect (encrypt + authenticate) an RTP packet.
|
|
4
|
+
*
|
|
5
|
+
* Output layout:
|
|
6
|
+
* RTP Header (unchanged) | Encrypted Payload | Auth Tag (10 or 4 bytes)
|
|
7
|
+
*
|
|
8
|
+
* GCM profiles are handled by delegating to `gcmSrtpProtect`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function srtpProtect(ctx: SrtpContext, rtpPacket: Buffer): Buffer;
|
|
11
|
+
/**
|
|
12
|
+
* Protect (encrypt + authenticate) an RTCP packet.
|
|
13
|
+
*
|
|
14
|
+
* Output layout:
|
|
15
|
+
* RTCP Header (8 bytes, unencrypted) |
|
|
16
|
+
* Encrypted Remainder |
|
|
17
|
+
* E (1 bit, always 1) | SRTCP Index (31 bits) |
|
|
18
|
+
* Auth Tag (10 bytes)
|
|
19
|
+
*/
|
|
20
|
+
export declare function srtcpProtect(ctx: SrtcpContext, rtcpPacket: Buffer): Buffer;
|
|
21
|
+
//# sourceMappingURL=protect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../src/protect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAqB,MAAM,YAAY,CAAC;AA8D1E;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAsCvE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAoC1E"}
|