package srtp
import (
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"fmt"
"github.com/pion/rtp"
)
type srtpCipherAeadAesGcm struct {
protectionProfileWithArgs
srtpCipher, srtcpCipher cipher .AEAD
srtpSessionSalt, srtcpSessionSalt []byte
mki []byte
srtpEncrypted, srtcpEncrypted bool
}
func newSrtpCipherAeadAesGcm(
profile protectionProfileWithArgs ,
masterKey , masterSalt , mki []byte ,
encryptSRTP , encryptSRTCP bool ,
) (*srtpCipherAeadAesGcm , error ) {
srtpCipher := &srtpCipherAeadAesGcm {
protectionProfileWithArgs : profile ,
srtpEncrypted : encryptSRTP ,
srtcpEncrypted : encryptSRTCP ,
}
srtpSessionKey , err := aesCmKeyDerivation (labelSRTPEncryption , masterKey , masterSalt , 0 , len (masterKey ))
if err != nil {
return nil , err
}
srtpBlock , err := aes .NewCipher (srtpSessionKey )
if err != nil {
return nil , err
}
srtpCipher .srtpCipher , err = cipher .NewGCM (srtpBlock )
if err != nil {
return nil , err
}
srtcpSessionKey , err := aesCmKeyDerivation (labelSRTCPEncryption , masterKey , masterSalt , 0 , len (masterKey ))
if err != nil {
return nil , err
}
srtcpBlock , err := aes .NewCipher (srtcpSessionKey )
if err != nil {
return nil , err
}
srtpCipher .srtcpCipher , err = cipher .NewGCM (srtcpBlock )
if err != nil {
return nil , err
}
if srtpCipher .srtpSessionSalt , err = aesCmKeyDerivation (
labelSRTPSalt , masterKey , masterSalt , 0 , len (masterSalt ),
); err != nil {
return nil , err
} else if srtpCipher .srtcpSessionSalt , err = aesCmKeyDerivation (
labelSRTCPSalt , masterKey , masterSalt , 0 , len (masterSalt ),
); err != nil {
return nil , err
}
mkiLen := len (mki )
if mkiLen > 0 {
srtpCipher .mki = make ([]byte , mkiLen )
copy (srtpCipher .mki , mki )
}
return srtpCipher , nil
}
func (s *srtpCipherAeadAesGcm ) encryptRTP (
dst []byte ,
header *rtp .Header ,
headerLen int ,
plaintext []byte ,
roc uint32 ,
rocInAuthTag bool ,
) (ciphertext []byte , err error ) {
payload := plaintext [headerLen :]
payloadLen := len (payload )
authTagLen , err := s .AEADAuthTagLen ()
if err != nil {
return nil , err
}
authPartLen := header .MarshalSize () + len (payload ) + authTagLen
dstLen := authPartLen + len (s .mki )
if rocInAuthTag {
dstLen += 4
}
dst = growBufferSize (dst , dstLen )
sameBuffer := isSameBuffer (dst , plaintext )
if !sameBuffer {
copy (dst , plaintext [:headerLen ])
}
iv := s .rtpInitializationVector (header , roc )
if s .srtpEncrypted {
s .srtpCipher .Seal (dst [headerLen :headerLen ], iv [:], payload , dst [:headerLen ])
} else {
clearLen := headerLen + payloadLen
if !sameBuffer {
copy (dst [headerLen :], payload )
}
s .srtpCipher .Seal (dst [clearLen :clearLen ], iv [:], nil , dst [:clearLen ])
}
if len (s .mki ) > 0 {
copy (dst [authPartLen :], s .mki )
}
if rocInAuthTag {
binary .BigEndian .PutUint32 (dst [len (dst )-4 :], roc )
}
return dst , nil
}
func (s *srtpCipherAeadAesGcm ) decryptRTP (
dst , ciphertext []byte ,
header *rtp .Header ,
headerLen int ,
roc uint32 ,
rocInAuthTag bool ,
) ([]byte , error ) {
authTagLen , err := s .AEADAuthTagLen ()
if err != nil {
return nil , err
}
rocLen := 0
if rocInAuthTag {
rocLen = 4
}
nDst := len (ciphertext ) - authTagLen - len (s .mki ) - rocLen
if nDst < headerLen {
return nil , ErrFailedToVerifyAuthTag
}
dst = growBufferSize (dst , nDst )
sameBuffer := isSameBuffer (dst , ciphertext )
iv := s .rtpInitializationVector (header , roc )
nEnd := len (ciphertext ) - len (s .mki ) - rocLen
if s .srtpEncrypted {
if _ , err := s .srtpCipher .Open (
dst [headerLen :headerLen ], iv [:], ciphertext [headerLen :nEnd ], ciphertext [:headerLen ],
); err != nil {
return nil , fmt .Errorf ("%w: %w" , ErrFailedToVerifyAuthTag , err )
}
} else {
nDataEnd := nEnd - authTagLen
if _ , err := s .srtpCipher .Open (
nil , iv [:], ciphertext [nDataEnd :nEnd ], ciphertext [:nDataEnd ],
); err != nil {
return nil , fmt .Errorf ("%w: %w" , ErrFailedToVerifyAuthTag , err )
}
if !sameBuffer {
copy (dst [headerLen :], ciphertext [headerLen :nDataEnd ])
}
}
if !sameBuffer {
copy (dst [:headerLen ], ciphertext [:headerLen ])
}
return dst , nil
}
func (s *srtpCipherAeadAesGcm ) encryptRTCP (dst , decrypted []byte , srtcpIndex uint32 , ssrc uint32 ) ([]byte , error ) {
authTagLen , err := s .AEADAuthTagLen ()
if err != nil {
return nil , err
}
aadPos := len (decrypted ) + authTagLen
dst = growBufferSize (dst , aadPos +srtcpIndexSize +len (s .mki ))
sameBuffer := isSameBuffer (dst , decrypted )
iv := s .rtcpInitializationVector (srtcpIndex , ssrc )
if s .srtcpEncrypted {
aad := s .rtcpAdditionalAuthenticatedData (decrypted , srtcpIndex )
if !sameBuffer {
copy (dst [:srtcpHeaderSize ], decrypted [:srtcpHeaderSize ])
}
copy (dst [aadPos :aadPos +srtcpIndexSize ], aad [8 :12 ])
s .srtcpCipher .Seal (dst [srtcpHeaderSize :srtcpHeaderSize ], iv [:], decrypted [srtcpHeaderSize :], aad [:])
} else {
if !sameBuffer {
copy (dst , decrypted )
}
binary .BigEndian .PutUint32 (dst [len (decrypted ):], srtcpIndex )
tag := make ([]byte , authTagLen )
s .srtcpCipher .Seal (tag [0 :0 ], iv [:], nil , dst [:len (decrypted )+srtcpIndexSize ])
copy (dst [aadPos :], dst [len (decrypted ):len (decrypted )+srtcpIndexSize ])
copy (dst [len (decrypted ):], tag )
}
copy (dst [aadPos +srtcpIndexSize :], s .mki )
return dst , nil
}
func (s *srtpCipherAeadAesGcm ) decryptRTCP (dst , encrypted []byte , srtcpIndex , ssrc uint32 ) ([]byte , error ) {
aadPos := len (encrypted ) - srtcpIndexSize - len (s .mki )
authTagLen , err := s .AEADAuthTagLen ()
if err != nil {
return nil , err
}
nDst := aadPos - authTagLen
if nDst < 0 {
return nil , ErrFailedToVerifyAuthTag
}
dst = growBufferSize (dst , nDst )
sameBuffer := isSameBuffer (dst , encrypted )
isEncrypted := encrypted [aadPos ]&srtcpEncryptionFlag != 0
iv := s .rtcpInitializationVector (srtcpIndex , ssrc )
if isEncrypted {
aad := s .rtcpAdditionalAuthenticatedData (encrypted , srtcpIndex )
if _ , err := s .srtcpCipher .Open (dst [srtcpHeaderSize :srtcpHeaderSize ], iv [:], encrypted [srtcpHeaderSize :aadPos ],
aad [:]); err != nil {
return nil , fmt .Errorf ("%w: %w" , ErrFailedToVerifyAuthTag , err )
}
} else {
dataEnd := aadPos - authTagLen
aad := make ([]byte , dataEnd +4 )
copy (aad , encrypted [:dataEnd ])
copy (aad [dataEnd :], encrypted [aadPos :aadPos +4 ])
if _ , err := s .srtcpCipher .Open (nil , iv [:], encrypted [dataEnd :aadPos ], aad ); err != nil {
return nil , fmt .Errorf ("%w: %w" , ErrFailedToVerifyAuthTag , err )
}
if !sameBuffer {
copy (dst [srtcpHeaderSize :], encrypted [srtcpHeaderSize :dataEnd ])
}
}
if !sameBuffer {
copy (dst [:srtcpHeaderSize ], encrypted [:srtcpHeaderSize ])
}
return dst , nil
}
func (s *srtpCipherAeadAesGcm ) rtpInitializationVector (header *rtp .Header , roc uint32 ) [12 ]byte {
var iv [12 ]byte
binary .BigEndian .PutUint32 (iv [2 :], header .SSRC )
binary .BigEndian .PutUint32 (iv [6 :], roc )
binary .BigEndian .PutUint16 (iv [10 :], header .SequenceNumber )
for i := range iv {
iv [i ] ^= s .srtpSessionSalt [i ]
}
return iv
}
func (s *srtpCipherAeadAesGcm ) rtcpInitializationVector (srtcpIndex uint32 , ssrc uint32 ) [12 ]byte {
var iv [12 ]byte
binary .BigEndian .PutUint32 (iv [2 :], ssrc )
binary .BigEndian .PutUint32 (iv [8 :], srtcpIndex )
for i := range iv {
iv [i ] ^= s .srtcpSessionSalt [i ]
}
return iv
}
func (s *srtpCipherAeadAesGcm ) rtcpAdditionalAuthenticatedData (rtcpPacket []byte , srtcpIndex uint32 ) [12 ]byte {
var aad [12 ]byte
copy (aad [:], rtcpPacket [:8 ])
binary .BigEndian .PutUint32 (aad [8 :], srtcpIndex )
aad [8 ] |= srtcpEncryptionFlag
return aad
}
func (s *srtpCipherAeadAesGcm ) getRTCPIndex (in []byte ) uint32 {
return binary .BigEndian .Uint32 (in [len (in )-len (s .mki )-srtcpIndexSize :]) &^ (srtcpEncryptionFlag << 24 )
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .