mirror of
https://github.com/aler9/gortsplib
synced 2025-12-24 13:38:08 +08:00
fill PayloadSPPolicyParamTypeAuthTagLen and PayloadSPPolicyParamTypeSessionAuthKeyLen properly.
276 lines
6.7 KiB
Go
276 lines
6.7 KiB
Go
package gortsplib
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/bluenviron/gortsplib/v5/pkg/mikey"
|
|
"github.com/bluenviron/gortsplib/v5/pkg/ntp"
|
|
"github.com/pion/rtcp"
|
|
"github.com/pion/rtp"
|
|
"github.com/pion/srtp/v3"
|
|
)
|
|
|
|
func mikeyGetPayload[T mikey.Payload](mikeyMsg *mikey.Message) (T, bool) {
|
|
var zero T
|
|
for _, wrapped := range mikeyMsg.Payloads {
|
|
if val, ok := wrapped.(T); ok {
|
|
return val, true
|
|
}
|
|
}
|
|
return zero, false
|
|
}
|
|
|
|
func mikeyGetSPPolicy(spPayload *mikey.PayloadSP, typ mikey.PayloadSPPolicyParamType) ([]byte, bool) {
|
|
for _, pl := range spPayload.PolicyParams {
|
|
if pl.Type == typ {
|
|
return pl.Value, true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func mikeyToContext(mikeyMsg *mikey.Message) (*wrappedSRTPContext, error) {
|
|
timePayload, ok := mikeyGetPayload[*mikey.PayloadT](mikeyMsg)
|
|
if !ok {
|
|
return nil, fmt.Errorf("time payload not present")
|
|
}
|
|
|
|
ts := ntp.Decode(timePayload.TSValue)
|
|
diff := time.Since(ts)
|
|
if diff < -time.Hour || diff > time.Hour {
|
|
return nil, fmt.Errorf("NTP difference is too high")
|
|
}
|
|
|
|
spPayload, ok := mikeyGetPayload[*mikey.PayloadSP](mikeyMsg)
|
|
if !ok {
|
|
return nil, fmt.Errorf("SP payload not present")
|
|
}
|
|
|
|
v, ok := mikeyGetSPPolicy(spPayload, mikey.PayloadSPPolicyParamTypeEncrAlg)
|
|
if !ok || !bytes.Equal(v, []byte{1}) {
|
|
return nil, fmt.Errorf("missing or unsupported policy: PayloadSPPolicyParamTypeEncrAlg")
|
|
}
|
|
|
|
v, ok = mikeyGetSPPolicy(spPayload, mikey.PayloadSPPolicyParamTypeSessionEncrKeyLen)
|
|
if !ok || !bytes.Equal(v, []byte{16}) {
|
|
return nil, fmt.Errorf("missing or unsupported policy: PayloadSPPolicyParamTypeSessionEncrKeyLen")
|
|
}
|
|
|
|
v, ok = mikeyGetSPPolicy(spPayload, mikey.PayloadSPPolicyParamTypeAuthAlg)
|
|
if !ok || !bytes.Equal(v, []byte{1}) {
|
|
return nil, fmt.Errorf("missing or unsupported policy: PayloadSPPolicyParamTypeAuthAlg")
|
|
}
|
|
|
|
v, ok = mikeyGetSPPolicy(spPayload, mikey.PayloadSPPolicyParamTypeSRTPEncrOffOn)
|
|
if !ok || !bytes.Equal(v, []byte{1}) {
|
|
return nil, fmt.Errorf("missing or unsupported policy: PayloadSPPolicyParamTypeSRTPEncrOffOn")
|
|
}
|
|
|
|
v, ok = mikeyGetSPPolicy(spPayload, mikey.PayloadSPPolicyParamTypeSRTCPEncrOffOn)
|
|
if !ok || !bytes.Equal(v, []byte{1}) {
|
|
return nil, fmt.Errorf("missing or unsupported policy: PayloadSPPolicyParamTypeSRTCPEncrOffOn")
|
|
}
|
|
|
|
v, ok = mikeyGetSPPolicy(spPayload, mikey.PayloadSPPolicyParamTypeSRTPAuthOffOn)
|
|
if !ok || !bytes.Equal(v, []byte{1}) {
|
|
return nil, fmt.Errorf("missing or unsupported policy: PayloadSPPolicyParamTypeSRTPAuthOffOn")
|
|
}
|
|
|
|
kemacPayload, ok := mikeyGetPayload[*mikey.PayloadKEMAC](mikeyMsg)
|
|
if !ok {
|
|
return nil, fmt.Errorf("KEMAC payload not present")
|
|
}
|
|
|
|
if len(kemacPayload.SubPayloads) != 1 {
|
|
return nil, fmt.Errorf("multiple keys are present")
|
|
}
|
|
|
|
if len(kemacPayload.SubPayloads[0].KeyData) != srtpKeyLength {
|
|
return nil, fmt.Errorf("unexpected key size: %d", len(kemacPayload.SubPayloads[0].KeyData))
|
|
}
|
|
|
|
ssrcs := make([]uint32, len(mikeyMsg.Header.CSIDMapInfo))
|
|
startROCs := make([]uint32, len(mikeyMsg.Header.CSIDMapInfo))
|
|
|
|
for i, entry := range mikeyMsg.Header.CSIDMapInfo {
|
|
ssrcs[i] = entry.SSRC
|
|
startROCs[i] = entry.ROC
|
|
}
|
|
|
|
srtpCtx := &wrappedSRTPContext{
|
|
key: kemacPayload.SubPayloads[0].KeyData,
|
|
ssrcs: ssrcs,
|
|
startROCs: startROCs,
|
|
}
|
|
err := srtpCtx.initialize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return srtpCtx, nil
|
|
}
|
|
|
|
func mikeyGenerate(ctx *wrappedSRTPContext) (*mikey.Message, error) {
|
|
csbID, err := randUint32()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msg := &mikey.Message{
|
|
Header: mikey.Header{
|
|
Version: 1,
|
|
CSBID: csbID,
|
|
},
|
|
}
|
|
|
|
msg.Header.CSIDMapInfo = make([]mikey.SRTPIDEntry, len(ctx.ssrcs))
|
|
|
|
n := 0
|
|
for _, ssrc := range ctx.ssrcs {
|
|
msg.Header.CSIDMapInfo[n] = mikey.SRTPIDEntry{
|
|
PolicyNo: 0,
|
|
SSRC: ssrc,
|
|
ROC: ctx.roc(ssrc),
|
|
}
|
|
n++
|
|
}
|
|
|
|
randData := make([]byte, 16)
|
|
_, err = rand.Read(randData)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
keyLen, err := ctx.profile.KeyLen()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
authKeyLen, err := ctx.profile.AuthKeyLen()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
authTagLen, err := ctx.profile.AuthTagRTPLen()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msg.Payloads = []mikey.Payload{
|
|
&mikey.PayloadT{
|
|
TSType: 0,
|
|
TSValue: ntp.Encode(time.Now()),
|
|
},
|
|
&mikey.PayloadRAND{
|
|
Data: randData,
|
|
},
|
|
&mikey.PayloadSP{
|
|
PolicyParams: []mikey.PayloadSPPolicyParam{
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeEncrAlg,
|
|
Value: []byte{1},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeSessionEncrKeyLen,
|
|
Value: []byte{byte(keyLen)},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeAuthAlg,
|
|
Value: []byte{1},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeSessionAuthKeyLen,
|
|
Value: []byte{byte(authKeyLen)},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeSRTPEncrOffOn,
|
|
Value: []byte{1},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeSRTCPEncrOffOn,
|
|
Value: []byte{1},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeSRTPAuthOffOn,
|
|
Value: []byte{1},
|
|
},
|
|
{
|
|
Type: mikey.PayloadSPPolicyParamTypeAuthTagLen,
|
|
Value: []byte{byte(authTagLen)},
|
|
},
|
|
},
|
|
},
|
|
&mikey.PayloadKEMAC{
|
|
SubPayloads: []*mikey.SubPayloadKeyData{
|
|
{
|
|
Type: mikey.SubPayloadKeyDataKeyTypeTEK,
|
|
KeyData: ctx.key,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return msg, nil
|
|
}
|
|
|
|
// srtp.Context with
|
|
// - accessible key
|
|
// - accessible SSRCs
|
|
// - mutex around Encrypt*, ROC*
|
|
type wrappedSRTPContext struct {
|
|
key []byte
|
|
ssrcs []uint32
|
|
startROCs []uint32
|
|
|
|
profile srtp.ProtectionProfile
|
|
w *srtp.Context
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
func (ctx *wrappedSRTPContext) initialize() error {
|
|
ctx.profile = srtp.ProtectionProfileAes128CmHmacSha1_80
|
|
|
|
var err error
|
|
ctx.w, err = srtp.CreateContext(ctx.key[:16], ctx.key[16:], ctx.profile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i, roc := range ctx.startROCs {
|
|
ctx.w.SetROC(ctx.ssrcs[i], roc)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ctx *wrappedSRTPContext) decryptRTP(dst []byte, encrypted []byte, header *rtp.Header) ([]byte, error) {
|
|
return ctx.w.DecryptRTP(dst, encrypted, header)
|
|
}
|
|
|
|
func (ctx *wrappedSRTPContext) decryptRTCP(dst []byte, encrypted []byte, header *rtcp.Header) ([]byte, error) {
|
|
return ctx.w.DecryptRTCP(dst, encrypted, header)
|
|
}
|
|
|
|
func (ctx *wrappedSRTPContext) encryptRTP(dst []byte, plaintext []byte, header *rtp.Header) ([]byte, error) {
|
|
ctx.mutex.Lock()
|
|
defer ctx.mutex.Unlock()
|
|
return ctx.w.EncryptRTP(dst, plaintext, header)
|
|
}
|
|
|
|
func (ctx *wrappedSRTPContext) encryptRTCP(dst []byte, decrypted []byte, header *rtcp.Header) ([]byte, error) {
|
|
ctx.mutex.Lock()
|
|
defer ctx.mutex.Unlock()
|
|
return ctx.w.EncryptRTCP(dst, decrypted, header)
|
|
}
|
|
|
|
func (ctx *wrappedSRTPContext) roc(ssrc uint32) uint32 {
|
|
ctx.mutex.RLock()
|
|
defer ctx.mutex.RUnlock()
|
|
v, _ := ctx.w.ROC(ssrc)
|
|
return v
|
|
}
|