HLS暗号化に使うSPEKEサーバーのChalice実装を行う

# はじめに

AWS MediaServices で動画を変換する選択肢には MediaConvert, MediaLive, MediaPackage がある。
これらを利用して HLS 暗号化した動画ファイルを作成しようとした場合、MediaConvert および MediaLive では暗号化に使う128bitの値を動画変換時のジョブに直接渡すことができる。 しかし、MediaConvert の場合はこれを渡すオプションが存在しない。

調べてみると、MediaConvert で HLS 暗号化変換を行うためには、SPEKE 仕様に準拠したキーサーバーを準備しなければならないことがわかった。 SPEKE とは何か、は以下の記事が非常にわかりやすかった。 一言でいえば、企業ごとに異なる仕様のインタフェースを共通のインタフェース企画に変換するためのアダプタ実装である。

https://dev.classmethod.jp/articles/aws-mediaservices-speke/

同記事内にあるが、SPEKE のサンプル実装とそれを実際に AWS にデプロイするために、CloudFormation が提供されている。 

https://github.com/awslabs/speke-reference-server

ただ単に使ってみるだけであれば、これでも良いのだが、より広いユースケースに対応できるようにするため、かなり広範囲のリソース (CloudFront, S3Bucket, SecretsManager) を生成して使うようになっている。 より小規模な SPEKEサーバー + S3の部分だけがほしかったので、ここのコードを参考にしつつ、HLS暗号化に使うための部分だけを抜き出した Chalice 実装を行ってみることにした。

# Chalice 実装

https://github.com/t-kigi/speke-chalice

詳細は README 参照。

# 使い方

デプロイ先の URL が `https://++++++++++.execute-api.ap-northeast-1.amazonaws.com/speke-server-dev/` である場合、MediaConvert であれば DRM 部分を以下のように設定する。

![SnapCrab_NoName_2021-6-17_23-16-33_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502582/efcb7658-45e4-eca6-7dfd-7a81ffda875f.png)

キーサーバー環境変数の `SYSTEM_ID` の値は `18f55148-668f-447a-b4f5-298869e84cf1` となっているので、システムID にはこの値を入力する。 定数初期化ベクトルには 128 bit (32 bytes) の16進数を入力するが、ここに入力した場合は「マニフェストの初期化ベクトル」を「含む」とすること。

このキーサーバーを使うことで、DRM暗号化設定の「リソースID」を別にすると、そのリソースID毎の鍵ファイルを自動で生成して S3 に保存してくれるため、動画毎に個別に鍵ファイルを用意する必要はなくなる。


## 補足: HLS暗号化における初期化ベクトル

HLS暗号化の設定では「初期化ベクトルを記載しない」という設定ができる。
これを設定すると、プレイリストマニフェストに初期化ベクトル `IV=xxxxxx` の部分が出力されない。
この記載がない場合、[HLSの仕様](https://tex2e.github.io/rfc-translater/html/rfc8216.html)に従い、分割された動画ファイルのシーケンス番号を暗号化ベクトルIVとして利用する。
そのため、「初期化ベクトルを記載しない」チェックを入れた場合、「定数初期化ベクトル」をデフォルトの設定とした状態で暗号化するということになる。 この場合は動画ファイルが正常に再生できる。
一方、「初期化ベクトルを記載しない」チェックを入れて「定数初期化ベクトル」をデフォルト **以外** に具体的な値を入れた場合は、**鍵ファイルを読み込んでも動画が再生できない** 。

> An EXT-X-KEY tag with a KEYFORMAT of "identity" that does not have an IV attribute indicates that the Media Sequence Number is to be used as the IV when decrypting a Media Segment, by putting its big-endian binary representation into a 16-octet (128-bit) buffer and padding (on the left) with zeros.

> IV属性を持たないKEYFORMATが「identity」のEXT-X-KEYタグは、ビッグエンディアンバイナリ表現をaに入れて、メディアセグメントを復号化するときにメディアシーケンス番号がIVとして使用されることを示します。 16オクテット（128ビット）のバッファーとゼロによるパディング（左側）。

仕組み的には、「プレイリストの `.m3u8` ファイルに初期化ベクトルを書き込まずに暗号化HLSファイルを作成し」「許可された人がプレイリストファイルをダウンロードするときに限り正しい IV 項目を書き加える」 という処理を挟めば、例え鍵が同一でもある程度、閲覧の制御が可能となる。


## 補足: XML入力における各パラメータの対応

キーサーバーの入力 XML を確認した限り、以下のような値が入力されているようだった。

| タグ | 属性 | 内容 |
|----------+------+-----|
| cpix:CPIX |  id | DRM変換で指定したリソースID | 
| cpix:ContentKey | kid | [APIの仕様](https://docs.aws.amazon.com/ja_jp/speke/latest/documentation/speke.pdf) では単に "キー ID。これは実際のキーではなく、ハッシュテーブルのキーを指す識別子です。" としか記載されていないが、MediaConvert で検証した結果、リソースIDが同じであれば、同じuuidが設定される挙動を見せた。 | 
| cpix:DRMSystem | kid | 同上。| 
| cpix:DRMSystem | systemId | DRM変換で指定したシステムID | 


# まとめ

前回の記事では事前に鍵を作って動画を変換するという手順だったが、このキーサーバーを用意して使うことで、鍵を別途作る必要はなくなる。

https://qiita.com/t-kigi/items/9c0620e80db79e88411e

また、どちらかというとこの手順で使うための SPEKE サーバーを作った感じではあるので、併せて読むと理解が深まると思う。


# 参考

https://tex2e.github.io/rfc-translater/html/rfc8216.html

https://docs.aws.amazon.com/ja_jp/speke/latest/documentation/speke.pdf

https://qiita.com/daisukeoda/items/9a8924b16c1dafcbc5f9