Repository: beardypig/pymp4

Issue:
Definition of tenc box seems invalid or too leniant
I've attached a sample `tenc` box from https://sho.com, specifically the show Yellowjackets S02E05 on the 2160p track.

![image](https://user-images.githubusercontent.com/17136956/233861866-8042fd19-2094-44e3-819c-dc47a88829d8.png)

It fails to parse with the following error:

```
Traceback (most recent call last):
  File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "C:\Program Files\Python310\Scripts\mp4dump.exe\__main__.py", line 7, in <module>
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\pymp4\cli.py", line 26, in dump
    box = Box.parse_stream(fd)
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\construct\core.py", line 186, in parse_stream
    return self._parse(stream, context, "parsing")
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\pymp4\parser.py", line 46, in _parse
    obj = self.subcon._parse(stream2, context, path)
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\construct\core.py", line 855, in _parse
    subobj = list(sc._parse(stream, context, path).items())
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\construct\core.py", line 297, in _parse
    return self.subcon._parse(stream, context, path)
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\construct\core.py", line 1544, in _parse
    obj = self.cases.get(key, self.default)._parse(stream, context, path)
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\construct\core.py", line 859, in _parse
    subobj = sc._parse(stream, context, path)
  File "C:\Users\User\AppData\Roaming\Python\Python310\site-packages\construct\core.py", line 2700, in _parse
    raise e.__class__("%s\n    %s" % (e, path))
construct.core.ConstError: expected 0 but parsed 25
    parsing -> _reserved1
```

It's failing at the byte 0xD with the value 0x19 (25 decimal). This offset would be the `_reserved1` definition.

[tenc_box_from_showtime_exact.zip](https://github.com/beardypig/pymp4/files/11304733/tenc_box_from_showtime_exact.zip)


Discussion:
codecov-commenter: ## [Codecov](https://codecov.io/gh/beardypig/pymp4/pull/30?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig) Report
> :exclamation: No coverage uploaded for pull request base (`master@47628e8`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig#section-missing-base-commit).
> Patch coverage: 85.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff            @@
##             master      #30   +/-   ##
=========================================
  Coverage          ?   74.09%           
=========================================
  Files             ?        5           
  Lines             ?      193           
  Branches          ?        0           
=========================================
  Hits              ?      143           
  Misses            ?       50           
  Partials          ?        0           
```


| [Impacted Files](https://codecov.io/gh/beardypig/pymp4/pull/30?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig) | Coverage Δ | |
|---|---|---|
| [src/pymp4/util.py](https://codecov.io/gh/beardypig/pymp4/pull/30?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig#diff-c3JjL3B5bXA0L3V0aWwucHk=) | `82.05% <78.57%> (ø)` | |
| [src/pymp4/parser.py](https://codecov.io/gh/beardypig/pymp4/pull/30?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig#diff-c3JjL3B5bXA0L3BhcnNlci5weQ==) | `83.07% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/beardypig/pymp4/pull/30?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=beardypig).


Patch:
diff --git a/src/pymp4/parser.py b/src/pymp4/parser.py
index 1c2311c..bfe2b50 100644
--- a/src/pymp4/parser.py
+++ b/src/pymp4/parser.py
@@ -694,18 +694,26 @@ ProtectionSystemHeaderBox = Struct(
 
 TrackEncryptionBox = Struct(
     "type" / If(this._.type != b"uuid", Const(b"tenc")),
-    "version" / Default(Int8ub, 0),
+    "version" / Default(OneOf(Int8ub, (0, 1)), 0),
     "flags" / Default(Int24ub, 0),
-    "_reserved0" / Const(Int8ub, 0),
-    "_reserved1" / Const(Int8ub, 0),
-    "is_encrypted" / Int8ub,
-    "iv_size" / Int8ub,
+    "_reserved" / Const(Int8ub, 0),
+    "default_byte_blocks" / Default(IfThenElse(
+        this.version > 0,
+        BitStruct(
+            # count of encrypted blocks in the protection pattern, where each block is 16-bytes
+            "crypt" / Nibble,
+            # count of unencrypted blocks in the protection pattern
+            "skip" / Nibble
+        ),
+        Const(Int8ub, 0)
+    ), 0),
+    "is_encrypted" / OneOf(Int8ub, (0, 1)),
+    "iv_size" / OneOf(Int8ub, (0, 8, 16)),
     "key_ID" / UUIDBytes(Bytes(16)),
-    "constant_iv" / Default(If(this.is_encrypted and this.iv_size == 0,
-                               PrefixedArray(Int8ub, Byte),
-                               ),
-                            None)
-
+    "constant_iv" / Default(If(
+        this.is_encrypted and this.iv_size == 0,
+        PrefixedArray(Int8ub, Byte)
+    ), None)
 )
 
 SampleEncryptionBox = Struct(


Test patch:
diff --git a/tests/test_dashboxes.py b/tests/test_dashboxes.py
index e1b014b..3500619 100644
--- a/tests/test_dashboxes.py
+++ b/tests/test_dashboxes.py
@@ -32,6 +32,8 @@ class BoxTests(unittest.TestCase):
             (type=b"tenc")
             (version=0)
             (flags=0)
+            (_reserved=0)
+            (default_byte_blocks=0)
             (is_encrypted=1)
             (iv_size=8)
             (key_ID=UUID('337b9643-21b6-4355-9e59-3eccb46c7ef7'))