Skip to content

Commit

Permalink
feat: PIFF decryption using uuid Sample Encryption Box
Browse files Browse the repository at this point in the history
  • Loading branch information
tobbee committed Dec 27, 2023
1 parent 3f43ac6 commit 8d91a0e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Support for decrypting PIFF-encrypted segments

### Fixed

- Parsing of AVCDecoderConfigurationRecord
Expand Down
8 changes: 7 additions & 1 deletion mp4/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,14 @@ func DecryptFragment(frag *Fragment, di DecryptInfo, key []byte) error {
if err != nil {
return err
}
var senc *SencBox
if traf.Senc != nil {
senc = traf.Senc
} else {
senc = traf.UUIDSenc.Senc
}

err = decryptSamplesInPlace(schemeType, samples, key, tenc, traf.Senc)
err = decryptSamplesInPlace(schemeType, samples, key, tenc, senc)
if err != nil {
return err
}
Expand Down
48 changes: 36 additions & 12 deletions mp4/traf.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type TrafBox struct {
Sbgp *SbgpBox
Sgpd *SgpdBox
Senc *SencBox
UUIDSenc *UUIDBox // A PIFF box of subtype senc
Trun *TrunBox // The first TrunBox
Truns []*TrunBox
Children []Box
Expand Down Expand Up @@ -59,23 +60,37 @@ func DecodeTrafSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, err
// ContainsSencBox - is there a senc box in traf and is it parsed
// If not parsed, call ParseReadSenc to parse it
func (t *TrafBox) ContainsSencBox() (ok, parsed bool) {
if t.Senc != nil {
return true, !t.Senc.readButNotParsed
for _, c := range t.Children {
switch box := c.(type) {
case *SencBox:
return true, !box.readButNotParsed
case *UUIDBox: // PIFF
if box.SubType() == "senc" {
return true, !box.Senc.readButNotParsed
}
}
}
return false, false
}

// ParseReadSenc makes a second round to parse a senc box previously read
func (t *TrafBox) ParseReadSenc(defaultIVSize byte, moofStartPos uint64) error {
if t.Senc == nil {
return fmt.Errorf("no senc box")
if t.Senc == nil && t.UUIDSenc == nil {
return fmt.Errorf("no senc box or uuid senc box")
}
var senc *SencBox
if t.Senc != nil {
senc = t.Senc
} else {
senc = t.UUIDSenc.Senc
}
if t.Saio != nil {
// saio should be present, but we try without it, if it doesn't exist
posFromSaio := t.Saio.Offset[0] + int64(moofStartPos)
if uint64(posFromSaio) != t.Senc.StartPos+16 {
if uint64(posFromSaio) != senc.StartPos+16 {
//TODO. Reenable
//return fmt.Errorf("offset from saio (%d) and moof differs from senc data start %d", posFromSaio, t.Senc.StartPos+16)
fmt.Printf("offset from saio (%d) and moof differs from senc data start %d", posFromSaio, t.Senc.StartPos+16)
//return fmt.Errorf("offset from saio (%d) and moof differs from senc data start %d", posFromSaio, senc.StartPos+16)
fmt.Printf("offset from saio (%d) and moof differs from senc data start %d", posFromSaio, senc.StartPos+16)

}
}
Expand Down Expand Up @@ -104,7 +119,7 @@ func (t *TrafBox) ParseReadSenc(defaultIVSize byte, moofStartPos uint64) error {
seigEntry := sgpdEntry.(*SeigSampleGroupEntry)
perSampleIVSize = seigEntry.PerSampleIVSize
}
err := t.Senc.ParseReadBox(perSampleIVSize, t.Saiz)
err := senc.ParseReadBox(perSampleIVSize, t.Saiz)
if err != nil {
return err
}
Expand Down Expand Up @@ -133,6 +148,10 @@ func (t *TrafBox) AddChild(child Box) error {
t.Trun = box
}
t.Truns = append(t.Truns, box)
case *UUIDBox:
if box.SubType() == "senc" {
t.UUIDSenc = box
}
default:
}
t.Children = append(t.Children, child)
Expand Down Expand Up @@ -258,16 +277,21 @@ func (t *TrafBox) RemoveEncryptionBoxes() uint64 {
remainingChildren := make([]Box, 0, len(t.Children))
var nrBytesRemoved uint64 = 0
for _, ch := range t.Children {
switch ch.Type() {
case "saiz":
switch box := ch.(type) {
case *SaizBox:
nrBytesRemoved += ch.Size()
t.Saiz = nil
case "saio":
case *SaioBox:
nrBytesRemoved += ch.Size()
t.Saio = nil
case "senc":
case *SencBox:
nrBytesRemoved += ch.Size()
t.Senc = nil
case *UUIDBox:
if box.SubType() == "senc" {
nrBytesRemoved += ch.Size()
t.UUIDSenc = nil
}
default:
remainingChildren = append(remainingChildren, ch)
}
Expand Down
29 changes: 26 additions & 3 deletions mp4/uuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const (

// UUIDTfrf - MSS tfrf UUID [MS-SSTR 2.2.4.5]
UUIDTfrf = "d4807ef2-ca39-4695-8e54-26cb9e46a79f"

// UUIDPiffSenc - PIFF UUID for Sample Encryption Box (PIFF 1.1 spec)
UUIDPiffSenc = "a2394f52-5a9b-4f14-a244-6c427c648df4"
)

// uuid - compact representation of UUID
Expand Down Expand Up @@ -62,8 +65,9 @@ func mustCreateUUID(u string) uuid {
}

var (
uuidTfxd uuid = mustCreateUUID(UUIDTfxd)
uuidTfrf uuid = mustCreateUUID(UUIDTfrf)
uuidTfxd uuid = mustCreateUUID(UUIDTfxd)
uuidTfrf uuid = mustCreateUUID(UUIDTfrf)
uuidPiffSenc uuid = mustCreateUUID(UUIDPiffSenc)
)

// UUIDBox - Used as container for MSS boxes tfxd and tfrf
Expand All @@ -72,6 +76,8 @@ type UUIDBox struct {
uuid uuid
Tfxd *TfxdData
Tfrf *TfrfData
Senc *SencBox
StartPos uint64
UnknownPayload []byte
}

Expand Down Expand Up @@ -117,7 +123,7 @@ func DecodeUUIDBox(hdr BoxHeader, startPos uint64, r io.Reader) (Box, error) {

// DecodeUUIDBoxSR - decode a UUID box including tfxd or tfrf
func DecodeUUIDBoxSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box, error) {
b := &UUIDBox{}
b := &UUIDBox{StartPos: startPos}
copy(b.uuid[:], sr.ReadBytes(16))
switch b.UUID() {
case UUIDTfxd:
Expand All @@ -132,6 +138,14 @@ func DecodeUUIDBoxSR(hdr BoxHeader, startPos uint64, sr bits.SliceReader) (Box,
return nil, err
}
b.Tfrf = tfrf
case UUIDPiffSenc:
// This is like a SencBox except that there is no size and type. Offset and sizes must be slightly adjusted.
subHdr := BoxHeader{"senc", hdr.Size - 16, 8}
box, err := DecodeSencSR(subHdr, b.StartPos+16, sr)
if err != nil {
return nil, fmt.Errorf("failed to decode senc in UUID: %w", err)
}
b.Senc = box.(*SencBox)
default:
b.UnknownPayload = sr.ReadBytes(int(hdr.Size) - 8 - 16)
}
Expand All @@ -152,6 +166,8 @@ func (b *UUIDBox) Size() uint64 {
size += b.Tfxd.size()
case u.Equal(uuidTfrf):
size += b.Tfrf.size()
case u.Equal(uuidPiffSenc):
size += b.Senc.Size() - 8 // -8 because no header
default:
size += uint64(len(b.UnknownPayload))
}
Expand Down Expand Up @@ -197,6 +213,8 @@ func (b *UUIDBox) SubType() string {
return "tfxd"
case u.Equal(uuidTfrf):
return "tfrf"
case u.Equal(uuidPiffSenc):
return "senc"
default:
return "unknown"
}
Expand Down Expand Up @@ -299,6 +317,11 @@ func (b *UUIDBox) Info(w io.Writer, specificBoxLevels, indent, indentStep string
for i := 0; i < int(b.Tfrf.FragmentCount); i++ {
bd.write(" - [%d]: absTime=%d absDur=%d", i+1, b.Tfrf.FragmentAbsoluteTimes[i], b.Tfrf.FragmentAbsoluteDurations[i])
}
case "senc":
err := b.Senc.Info(w, specificBoxLevels, indent+" ", indentStep)
if err != nil {
return fmt.Errorf("piff senc: %w", err)
}
default:
bd.write(" - payload: %s", hex.EncodeToString(b.UnknownPayload))
}
Expand Down

0 comments on commit 8d91a0e

Please sign in to comment.