// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package srtp implements Secure Real-time Transport Protocol
package srtp import ( ) /* Simplified structure of SRTP Packets: - RTP Header (with optional RTP Header Extension) - Payload (with optional padding) - AEAD Auth Tag - used by AEAD profiles only - MKI (optional) - Auth Tag - used by non-AEAD profiles only. When RCC is used with AEAD profiles, the ROC is sent here. */ func ( *Context) (, []byte, *rtp.Header, int) ([]byte, error) { , := .cipher.AuthTagRTPLen() if != nil { return nil, } , := .cipher.AEADAuthTagLen() if != nil { return nil, } := len(.sendMKI) var bool , = .hasROCInPacket(, ) // Verify that encrypted packet is long enough if len() < ( + + + ) { return nil, fmt.Errorf("%w: %d", errTooShortRTP, len()) } := .getSRTPSSRCState(.SSRC) var uint32 var int64 var uint64 if ! { // The ROC is not sent in the packet. We need to guess it. , , _ = .nextRolloverCount(.SequenceNumber) = (uint64() << 16) | uint64(.SequenceNumber) } else { // Extract ROC from the packet. The ROC is sent in the first 4 bytes of the auth tag. = binary.BigEndian.Uint32([len()-:]) = (uint64() << 16) | uint64(.SequenceNumber) = int64(.index) - int64() //nolint:gosec } , := .replayDetector.Check() if ! { return nil, &duplicatedError{ Proto: "srtp", SSRC: .SSRC, Index: uint32(.SequenceNumber), } } := .cipher if len(.mkis) > 0 { // Find cipher for MKI := [len()-- : len()-] , = .mkis[string()] if ! { return nil, ErrMKINotFound } } = growBufferSize(, len()--len(.sendMKI)) , = .decryptRTP(, , , , , ) if != nil { return nil, } () .updateRolloverCount(.SequenceNumber, , , ) return , nil } // DecryptRTP decrypts a RTP packet with an encrypted payload. func ( *Context) (, []byte, *rtp.Header) ([]byte, error) { if == nil { = &rtp.Header{} } , := .Unmarshal() if != nil { return nil, } return .decryptRTP(, , , ) } // EncryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided. // If the dst buffer does not have the capacity to hold `len(plaintext) + 10` bytes, // a new one will be allocated and returned. // If a rtp.Header is provided, it will be Unmarshaled using the plaintext. func ( *Context) ( []byte, []byte, *rtp.Header) ([]byte, error) { if == nil { = &rtp.Header{} } , := .Unmarshal() if != nil { return nil, } return .encryptRTP(, , , ) } // encryptRTP marshals and encrypts an RTP packet, writing to the dst buffer provided. // If the dst buffer does not have the capacity, a new one will be allocated and returned. // Similar to above but faster because it can avoid unmarshaling the header and marshaling the payload. func ( *Context) ( []byte, *rtp.Header, int, []byte, ) ( []byte, error) { := .getSRTPSSRCState(.SSRC) , , := .nextRolloverCount(.SequenceNumber) if { // ... when 2^48 SRTP packets or 2^31 SRTCP packets have been secured with the same key // (whichever occurs before), the key management MUST be called to provide new master key(s) // (previously stored and used keys MUST NOT be used again), or the session MUST be terminated. // https://www.rfc-editor.org/rfc/rfc3711#section-9.2 return nil, errExceededMaxPackets } .updateRolloverCount(.SequenceNumber, , false, 0) := false if .rccMode != RCCModeNone && .SequenceNumber%.rocTransmitRate == 0 { = true } return .cipher.encryptRTP(, , , , , ) } func ( *Context) ( *rtp.Header, int) (bool, int) { := false switch .rccMode { case RCCMode2: // This mode is supported for AES-CM and NULL profiles only. The ROC is sent in the first 4 bytes of the auth tag. = .SequenceNumber%.rocTransmitRate == 0 case RCCMode3: // This mode is supported for AES-GCM only. The ROC is sent as 4-byte auth tag. = .SequenceNumber%.rocTransmitRate == 0 if { = 4 } default: } return , }