Facing issues in decrypting SRTP packets using SRTP_AES128_CM_HMAC_SHA1_80 profile

I am trying to implement DTLS-SRTP in Node.js. The client is a Chrome WebRTC agent and my Node.js implementation acts as the DTLS server.

The client sends a RTP stream consisting of VP8 packets to my implementation and I am trying to decrypt it.

SRTP profile: SRTP_AES128_CM_HMAC_SHA1_80 (0x0001)
Key derivation rate (kdr) : 0

I am not able to decrypt the SRTP packets from the client.

Since my context is DTLS-SRTP, the Key derivation rate is 0. RFC 5764 4.1.2

Since Key Derivation Rate (kdr) is zero, the packet sequence number does not matter since index DIV kdr is always zero. (Where DIV is defined in RFC 3711)

Here is my implementation of SRTP master keys and session keys derivation according to RFC 5764

const srtpMasterKeyLength = 16; // bytes
const srtpMasterSaltLength = 14; // bytes

const profileConstants = {
    0x0001: 'SRTP_AES128_CM_HMAC_SHA1_80'
}

const profileParams = {
    SRTP_AES128_CM_HMAC_SHA1_80: {
        cipher: "AES_128_CM",
        cipherKeyLengthBits: 128,
        cipherSaltLengthBits: 112,
        maximumLifetime: 2 ** 31,
        authFunction: "HMAC_SHA1",
        authKeyLengthBits: 160,
        authTagLengthBits: 80,
    }
}

const tlsExtractorLabel = Buffer.from('EXTRACTOR-dtls_srtp', 'utf8');

function deriveSRTPKeys(packetIndex){
    packetIndex = packetIndex || 0;
    const seed = Buffer.concat([tlsData.clientRandom, tlsData.serverRandom]);
    const srtpKeyBlock = PRF(tlsData.masterSecret, tlsExtractorLabel, seed, 2 * (srtpMasterKeyLength + srtpMasterSaltLength));

    const srtpClientWriteMasterKey = srtpKeyBlock.slice(0, srtpMasterKeyLength);
    const srtpServerWriteMasterKey = srtpKeyBlock.slice(srtpMasterKeyLength, 2 * srtpMasterKeyLength);
    const srtpClientWriteMasterSalt = srtpKeyBlock.slice(2 * srtpMasterKeyLength, 2 * srtpMasterKeyLength + srtpMasterSaltLength);
    const srtpServerWriteMasterSalt = srtpKeyBlock.slice(2 * srtpMasterKeyLength + srtpMasterSaltLength, 2 * (srtpMasterKeyLength + srtpMasterSaltLength));

    srtpParams.clientKeys = srtpKDF(srtpClientWriteMasterKey, srtpClientWriteMasterSalt, packetIndex);
    srtpParams.serverKeys = srtpKDF(srtpServerWriteMasterKey, srtpServerWriteMasterSalt, packetIndex);
}


function srtpKDF(masterKey, masterSalt, index){

    const currentProfile = profileConstants[tlsData.srtpProfile];
    const currentProfileParams = profileParams[currentProfile];

    const srtpEncryptionKey = srtpKDFForLabel(masterKey, masterSalt, Buffer.from([0x00]), index, currentProfileParams.cipherKeyLengthBits / 8);
    const srtpAuthenticationKey = srtpKDFForLabel(masterKey, masterSalt, Buffer.from([0x01]), index, currentProfileParams.authKeyLengthBits / 8);
    const srtpSaltKey = srtpKDFForLabel(masterKey, masterSalt, Buffer.from([0x02]), index, currentProfileParams.cipherSaltLengthBits / 8);

    const srtcpEncryptionKey = srtpKDFForLabel(masterKey, masterSalt, Buffer.from([0x03]), index, currentProfileParams.cipherKeyLengthBits / 8);
    const srtcpAuthenticationKey = srtpKDFForLabel(masterKey, masterSalt, Buffer.from([0x04]), index, currentProfileParams.authKeyLengthBits / 8);
    const srtcpSaltKey = srtpKDFForLabel(masterKey, masterSalt, Buffer.from([0x05]), index, currentProfileParams.cipherSaltLengthBits / 8);

    return {
        srtpEncryptionKey,
        srtpAuthenticationKey,
        srtpSaltKey,
        srtcpEncryptionKey,
        srtcpAuthenticationKey,
        srtcpSaltKey
    }

}

const DIV = (a, t) => t ? Math.floor(a / t) : 0;

function srtpKDFForLabel(masterKey, masterSalt, labelBuffer, index, length) {
    const r = DIV(index, keyDerivationRate);
    const rBuffer = Buffer.alloc(6);
    rBuffer.writeUIntBE(r, 0, 6);

    const keyId = Buffer.concat([labelBuffer, rBuffer]);

    // we need to XOR keyId with master salt.
    // master salt is 14 bytes long, keyId is 7 bytes long.
    // we need to pad keyId with 7 zero on the left.
    const paddedKeyId = Buffer.alloc(14);
    keyId.copy(paddedKeyId, 7);

    const x = Buffer.alloc(14);
    for (let i = 0; i < 14; i++) {
        x[i] = masterSalt[i] ^ paddedKeyId[i];
    }

    const fillSize = Math.max(length - 14, 2);
    const input = Buffer.concat([x, Buffer.alloc(fillSize)]);
    const cipher = crypto.createCipheriv('aes-128-ecb', masterKey, null);
    const encrypted = Buffer.concat([cipher.update(input), cipher.final()]);

    if(!length){
        return Buffer.from([]);
    }

    // Return the requested length
    return encrypted.slice(0, length);

}

For the below values of tlsData and packetIndex,

masterSecret: <Buffer 8c 7b 04 58 1f 64 b5 a3 b8 2f 9a 19 4c a7 f8 0b ec ab 55 4f f5 4e 46 c2 c0 47 db 76 3e 74 2b 63 3f f2 41 e4 0e a5 b2 ae 02 01 42 05 96 55 1b 15>
clientRandom: <Buffer b4 ac 79 c9 28 0e dc 25 c7 92 b9 a2 7f ed 59 a4 ec e5 c5 60 01 ce fc 45 96 e4 d2 a8 2a 3e 4d 3c>
serverRandom: <Buffer 61 cc 55 54 a2 bc 49 a8 5b b1 91 48 10 ef fe f8 aa 2c 7b 6f 6f 7f 43 66 47 69 2d f5 56 08 58 cb>
srtpProfile: 0x0001

these are the srtpMasterKeys I got,

srtpClientWriteMasterKey : d4c0af3f5c00765d639adc8dd9eb73f5
srtpServerWriteMasterKey : e5ded7a3ae6e472ae3aff1abbed8ec08
srtpClientWriteMasterSalt: c2d34663023e8ad0fcc1318b931f
srtpServerWriteMasterSalt: 9d9eb0b02be74451996c2fa482aa

With the above master keys, I have derived the following session keys,

client - srtpEncryptionKey  : a91932af5e332477f61bd4114b3ccf44
client - srtpAuthKey        : 4403e333322ef447ec366adbf1c604a73dc35948
client - srtpSaltKey        : c481ad61727fe97a8108f59183a9
client - srtcpEncryptionKey : 5281eebcc430f1c4ae639e7e69a17657
client - srtcpAuthKey       : 7f262e298aea66dcb3dd50daa53faf433dc35948
client - srtcpSaltKey       : 5c5f4c6bec5d8f08ccbfc80beb49

server - srtpEncryptionKey  : d001aa61b70de77e64d28adb25eed225
server - srtpAuthKey        : e608b0730dcebb6fe227615e61a118d4a953426f
server - srtpSaltKey        : 39084612afe1746447ba94070cec
server - srtcpEncryptionKey : df38b45c19377474ded019b469b6112c
server - srtcpAuthKey       : f4c1bee2aad8478c496301a45ca48fd4a953426f
server - srtcpSaltKey       : e1f0979ac8041f76d1af3ee10da5

Here is my implementation to decrypt SRTP packets.

function decryptSRTP(packet){

    const sequenceNumber = packet.readUInt16BE(2);

    // 12 bytes RTP header and 10 bytes Auth Tag
    const payloadLength = packet.length - 12 - 10;

    const ssrc = packet.readUInt32BE(8);
    const ssrcBuffer = Buffer.alloc(4);
    ssrcBuffer.writeUIntBE(ssrc, 0, 4);

    const index = ((2 ** 16) * ROC) + sequenceNumber;
    const indexBuffer = Buffer.alloc(6);
    indexBuffer.writeUIntBE(index, 0, 6);

    const keyStreamBuffer = getKeyStream(
        srtpParams.clientKeys.srtpEncryptionKey, 
        srtpParams.clientKeys.srtpSaltKey, 
        ssrcBuffer, indexBuffer, 
        payloadLength
    );

    const encryptedPayload = packet.slice(12, packet.length - 10);
    const authTag = packet.slice(packet.length - 10);

    // verify auth tag
    const hmac = crypto.createHmac('sha1', srtpParams.clientKeys.srtpAuthenticationKey);
    hmac.update(encryptedPayload);
    const calculatedAuthTag = hmac.digest();

    if(!calculatedAuthTag.equals(authTag)){
        console.error('Auth tag mismatch');
    }

    const decryptedPayload = Buffer.alloc(encryptedPayload.length);

    for(let i = 0; i < encryptedPayload.length; i++){
        decryptedPayload[i] = encryptedPayload[i] ^ keyStreamBuffer[i];
    }

    console.log('authTag         :', authTag.toString('hex'));
    console.log('encryptedPayload:', encryptedPayload);
    console.log('decryptedPayload:', decryptedPayload);

}

function getKeyStream(encryptionKeyBuffer, saltingKeyBuffer, ssrcBuffer, indexBuffer, length){
    const keyStreamBuffer = Buffer.alloc(length);
    const blockCount = Math.ceil(length / 16);


    for(let i = 0; i < blockCount; i++){
        const counter = i;
        const keyStreamBlock = getKeyStreamBlock(encryptionKeyBuffer, saltingKeyBuffer, ssrcBuffer, indexBuffer, counter);
        const start = i * 16;
        const end = Math.min(start + 16, length);
        keyStreamBlock.copy(keyStreamBuffer, start, 0, end); // optimize this copy operation
    }

    return keyStreamBuffer;
}

function getKeyStreamBlock(encryptionKeyBuffer, saltingKeyBuffer, ssrcBuffer, packetIndexBuffer, counter){

    //IV = (k_s * 2^16) XOR (SSRC * 2^64) XOR (i * 2^16)
    const ivSegment = Buffer.alloc(16);

    for(let i = 0; i < 16; i++){

        const saltByte = saltingKeyBuffer[i] || 0;
        const ssrcByte = ssrcBuffer[i] || 0;
        const packetIndexByte = packetIndexBuffer[i] || 0;

        ivSegment[i] = saltByte ^ ssrcByte ^ packetIndexByte
        
    }

    addNumberToBuffer(ivSegment, counter);

    const cipher = crypto.createCipheriv('aes-128-ctr', encryptionKeyBuffer, ivSegment);
    cipher.setAutoPadding(false);

    const keyStreamBlock = Buffer.concat([
        cipher.update(Buffer.alloc(16)),
        cipher.final()
    ])

    return keyStreamBlock;
}

Here is a sample RTP packet that I tried to decrypt with above values including RTP headers at the start and SRTP auth tag at the end. (Packet #15, seqNo = 12998 from the dump attached below)

0000   80 e0 32 c6 9c 78 c3 4e f1 b5 d5 44 6e 03 8a c3
0010   b9 f6 b3 28 18 1f 60 8d df d6 ea f2 89 78 8a 88
0020   58 be 43 19 56 c4 50 92 2b 9e cd 94 0e 4a df c8
0030   2b 64 c5 e6 0e 8f 2a ea 0d 00 a4 72 23 b9 4e b4
0040   fd 36 61 72 5c fd f7 f0 7e 4c 54 51 06 e9 ac 38
0050   af a7 78 db cc b9 21 2f 9e f0 fe d0 94 8b 1c 9d
0060   55 57 a9 ff a0 31 4c ea 32 3e 14 73 c9 ac 66 0d
0070   32 54 01 b6 b0 cb 03 e8 46 eb 03 38 1a 84 2b 90
0080   8d ac 34 89 73 79 03 73 b7 4a 69 e9 6b 4e c0 f7
0090   6f 0b da cf 0e 92 ca 2a de 19 51 5f d6 67 ae 21
00a0   38 37 dc 61 3f 9e 56 e2 ae 0f 3c 5d a4 60 c8 49
00b0   45 24 38 c3 86 c3 ff 0f 63 0f 57 af 73 01 93 4f
00c0   92 c0 a1 6c 2e 5f e4 be ce 0b 6b f3 85 34 ac cd
00d0   fb 40 9f a5 66 6f 65 8c 0f df fb 69 9b 46 7a 55

While decrypting, the auth tag verification is failing and the decrypted packet’s contents are not what I expected. (This is a VP8 stream, so I expect the first byte to be 90).

I verified the functions srtpKDFForLabel() and getKeyStreamBlock() with the test vectors from in RFC 3711, appendix B.2 and B.3
and it works fine. I also exported key material from client end using openssl s_client -keymatexport Extractor-dtls_srtp and and verified it with my keyBlock. It also works fine.

Here is a PCAP dump of this session in case if it is required.

What am I doing wrong? Any help would be much appreciated.

Thanks in advance.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật