🐡
UnityのPlayAssetDelivaryで暗号化されたアセバンを扱う方法
googleのPlay Asset Delibary(PAD)を使用して独自に暗号化されたアセバンを読み込む方法の覚書です。
結論
PlayAssetPackRequest#getassetlocation
で保存先のパス/Offset/Sizeが取れるので、
そこからアセットパックを切り出します。
環境
unity 2021.3.17
google pad sdk 1.7.0
PADでアセバンを扱う方法
まずは通常のシナリオです。
公式マニュアルに記載されている通り、AssetBundleである場合はPlayAssetPackRequest.LoadAssetBundleAsync
、PlayAssetDelivery.RetrieveAssetBundleAsync
が使用できます。
アセットが AssetBundle である場合は、PlayAssetPackRequest.LoadAssetBundleAsync(assetPath) コンビニエンス メソッドを使用できます。メソッドに渡すアセットパスは、アセットパック内の AssetBundle へのパスに対応している必要があります。これにより、AssetBundleCreateRequest が返されます。
デモでも提供されているこれらのAPIは暗号化をかけると使うことができなくなります。
これは内部でUnityのAssetBundleAPIを直に使用しており、復号化の処理を差し込めないからです。
PADで暗号化されたアセバンを扱う方法
暗号化Streamを用意して復号化とAssetBundleの読み込みを行うシナリオです。
getassetlocation
から保存先のパス/Offset/Sizeを取得してアセットを切り出します。
//この実装はサンプル実装のため、実際には使用しないでください。
PlayAssetPackRequest request = PlayAssetDelivery.RetrieveAssetPackAsync("streaming");
var location = request.GetAssetLocation("prefabs/streamingchara_test_streaming");
var bytes = File.ReadAllBytes(location.Path).AsSpan((int)location.Offset,(int)location.Size).ToArray();
var stream = new MemoryStream(bytes);
var bundleCryptStream = new BundleCryptStream(stream);
var assetBundle = AssetBundle.LoadFromStream(bundleCryptStream);
サンプルで使用しているBundleCryptStreamは以下になります。
BundleCryptStream
// 既存Streamを上書きして暗号化と複合化を行うストリーム
// 既存ストリームをもとに作成した場合該当ストリームの解放責任を持つ
// 参考 : https://gist.github.com/tsubaki/4c15f08f592fc303cce97f13c686243f
public class BundleCryptStream : Stream
{
private Stream _baseStream;
public BundleCryptStream(Stream baseStream)
{
_baseStream = baseStream;
}
~BundleCryptStream()
{
Dispose(true);
}
public override void Flush() => _baseStream.Flush();
public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);
public override void SetLength(long value) => _baseStream.SetLength(value);
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length => _baseStream.Length;
public override long Position
{
get => _baseStream.Position;
set => _baseStream.Position = value;
}
public override int Read(byte[] buffer, int offset, int count)
{
var ret = _baseStream.Read(buffer, offset, count);
ApplyXOR(buffer,offset,count);
return ret;
}
public override void Write(byte[] buffer, int offset, int count)
{
ApplyXOR(buffer,offset,count);
_baseStream.Write(buffer, offset, count);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_baseStream?.Dispose();
}
base.Dispose(disposing);
}
private void ApplyXOR(byte[] buffer, int offset, int count)
{
if (buffer is null || buffer.Length == 0 || count == 0)
{
return ;
}
//サンプルkeyなのでハードコードしてるが、runtime導出の方がセキュア
byte kay = 110;
for (int i = offset; i < count; i++)
{
buffer[i] ^= kay;
}
}
}
Discussion