Is it possible that gsth264parser and gsth265parser parse encrypted nal data(header isn't encrypted)?

I wanna make pipeline for secure video path and use gsth264parser, gsth265parser.
example :
appsrc → qtdemux → cencdrm → h264parser → videodecryptor → videodecoder…
video data isn’t decrypted in cencdrm plugin. (passthrough)

I suppose you can make one of your own - however no security promises. A couple of things I would check are:

  • Emulation prevention bytes. These may break your NAL unit post-encryption, making the stream unplayable.
  • gst h264/h265 nal parser for accessing nal units to encode without breaking h264 stream itself.
  • Possible attacks on the encrypted stream. It may be possible to partially or completely retrieve what is in the video.

The hardest part is ensuring the security of the video encryptor i think. The rest can be easily achieved.

There should be many existent solutions for similar tasks i think but unfortunately I do not know any.

Thank you kindly reply.
Actually I concerned about below case.

Pre-condition : input nal header isn’t encrypted
input : encryped h264 AVC format data → h264 parser → output : encrypted h264 AnnexB format data

Could you give your opinion on whether there will be any problems with the operation of the h264 parser in the case above?

Honestly, I am not very knowledgable about how AVC ↔ AnnexB conversion works. The following are just my guesses.

If the encrypted NAL unit contents (data, not the header) do not change after the conversion, it should work. I quickly looked at the differences between them, and saw that they both use emulation 3 bytes (h.265 - do you need remove emulation prevention bytes of H.264 stream in AVCC or HVCC format? - Stack Overflow) thus there should not be an issue because of them.

On the other hand, there seems to be other differences between them (video streaming - what the advantage of h264 Annex-B VS AVCC - Stack Overflow) but then again, we are discussing only encrypting the video data, thus everything else should remain intact and GStreamer h264parse should be able to do its work.

I think it is worth a shot. I would love to try this some time.

Widevine and Playready works by encrypting the entropy coded part of the stream, and I’m pretty sure it’s being de-emulated (and that decrypters takes care of removing the post encryption de-emulation bytes). Small clarification about AVCc, the inner data is still de-emulated.

Thank you for your kind responses.
As per your suggestion, I plan to give it a try first.

To explain the current situation, we are delivering H264 data in AVCC format encrypted with PlayReady DRM from the Player. However, the video decoder needs to receive data in AnnexB format, which includes a start code. Therefore, we are in a situation where we need to parse the data in AVCC format and convert it to AnnexB format. However, because we can’t decrypt in the user area for security reasons, we have to parse and convert the encrypted data.

The biggest concern is whether there is a part in gsth264parser that references the nal payload, not the nal header. Could I get your opinion on this?

gst_h264_parser_identify_nalu_avc function can be used to get GstH264NalUnit struct which has “data” field, which is what you are looking for.

I’d need to check, but at least with widewine encryption it is not an issue to convert the stream format before the decrypter.

So starting from Playready 3, common encryption is used.

IEC-23001:

Subsample encryption is specified for NAL structured video, such as AVC and HEVC, to enable normal
processing and editing of video elementary streams prior to decryption.

The MPEG codecs specifications are not public, but the VP8/9 spec illustrate the method quite well:

https://www.webmproject.org/vp9/mp4/

I started making a plugin for this and having issues with stream format conversion. Even putting a single h264parse that supposedly do nothing makes the stream stall. I have not yet investigated it but I made the repository public and it is here.

You can test it with the following:

gst-launch-1.0 videotestsrc ! nvh264enc ! \
    h264encrypt iv=01234567012345670123456701234567 key=01234567012345670123456701234567 encryption-mode=aes-ctr ! \
        h264parse  ! \
    h264encrypt iv=01234567012345670123456701234567 key=01234567012345670123456701234567 encryption-mode=aes-ctr ! \
    nvh264dec ! glimagesink

Other modes, ECB and CBC, do not even produce a single frame.
If I do stream format conversion, not any of the modes produces a single frame.

Is the double encrypt a typo?

I just dived quickly in the encryptor code and it won’t work with h264parse. The common encryption standard requires that only the rbsp data get encrypted. For H.264 it means you need to enforce byte aligned RBSP (or use CABAC, which requires it).

The encryptor you have only skips the NAL header, it needs to skip the slice header.

No, in counter mode a bit string is generated using the cipher and this string is used to XOR the plaintext. Thus, doing this for a second time decrypts it.

I see. I quickly checked what I could use to see where the slice header ends; however, could not find anything I can use. nalreader and nalwriter are marked G_GNUC_INTERNAL thus I cannot use them. Maybe I can use them or gst_h264_bit_writer as examples for parsing slice header myself. Do you have suggestions for this?

On the other hand, I started encrypting from nalu.offset + nalu.header_bytes + 8 to test what would happen if I were skipping slice headers and saw that the stream format conversion works for ECB and CTR modes. CBC mode crashes due to invalid reads / writes, which is odd because they do not exist on CTR mode according to valgrind. Maybe this means that I am using the AES cipher wrongly and it is out of topic here.

The example stream conversion pipelines that work when encryption starts 8 bytes after nalu start:

gst-launch-1.0 videotestsrc ! nvh264enc ! \
    h264encrypt iv=01234567012345670123456701234567 key=01234567012345670123456701234567 encryption-mode=aes-ctr ! \
    h264parse ! video/x-h264,stream-format=avc3 ! h264parse ! video/x-h264,stream-format=byte-stream ! \
    h264encrypt iv=01234567012345670123456701234567 key=01234567012345670123456701234567 encryption-mode=aes-ctr ! \
    nvh264dec ! glimagesink

gst-launch-1.0 videotestsrc ! nvh264enc ! \
    h264encrypt iv=01234567012345670123456701234567 key=01234567012345670123456701234567 encryption-mode=aes-ecb ! \
    h264parse ! video/x-h264,stream-format=avc3 ! h264parse ! video/x-h264,stream-format=byte-stream ! \
    h264encrypt iv=01234567012345670123456701234567 key=01234567012345670123456701234567 encryption-mode=aes-ecb-dec ! \
    nvh264dec ! glimagesink

The only way to find the size of the slice header is to parse it. The libgstcodecparsers library have function for that.