Skip to content

Commit

Permalink
Add support for pattern encryption and default initialization vectors
Browse files Browse the repository at this point in the history
This will extend our CENC modes support to cbcs and cens. The change was
not split into two different CLs due to lack of test content for
default initialization vectors, aside from AES-CBCS encrypted ones.

Issue:#1661
Issue:#1989
Issue:#2089

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=159810371
  • Loading branch information
AquilesCanta authored and ojw28 committed Jun 22, 2017
1 parent 73b17a7 commit aca80b8
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 43 deletions.
24 changes: 24 additions & 0 deletions demo/src/main/assets/media.exolist.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,30 @@
"uri": "https://storage.googleapis.com/wvmedia/cbc1/h264/tears/tears_aes_cbc1_uhd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure SD & HD (cbcs,MP4,H264)",
"uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure SD (cbcs,MP4,H264)",
"uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_sd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure HD (cbcs,MP4,H264)",
"uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_hd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
},
{
"name": "WV: Secure UHD (cbcs,MP4,H264)",
"uri": "https://storage.googleapis.com/wvmedia/cbcs/h264/tears/tears_aes_cbcs_uhd.mpd",
"drm_scheme": "widevine",
"drm_license_url": "https://proxy.uat.widevine.com/proxy?provider=widevine_test"
}
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ public final class CryptoInfo {
/**
* @see android.media.MediaCodec.CryptoInfo.Pattern
*/
public int patternBlocksToEncrypt;
public int encryptedBlocks;
/**
* @see android.media.MediaCodec.CryptoInfo.Pattern
*/
public int patternBlocksToSkip;
public int clearBlocks;

private final android.media.MediaCodec.CryptoInfo frameworkCryptoInfo;
private final PatternHolderV24 patternHolder;
Expand All @@ -70,28 +70,20 @@ public CryptoInfo() {
* @see android.media.MediaCodec.CryptoInfo#set(int, int[], int[], byte[], byte[], int)
*/
public void set(int numSubSamples, int[] numBytesOfClearData, int[] numBytesOfEncryptedData,
byte[] key, byte[] iv, @C.CryptoMode int mode) {
byte[] key, byte[] iv, @C.CryptoMode int mode, int encryptedBlocks, int clearBlocks) {
this.numSubSamples = numSubSamples;
this.numBytesOfClearData = numBytesOfClearData;
this.numBytesOfEncryptedData = numBytesOfEncryptedData;
this.key = key;
this.iv = iv;
this.mode = mode;
patternBlocksToEncrypt = 0;
patternBlocksToSkip = 0;
this.encryptedBlocks = encryptedBlocks;
this.clearBlocks = clearBlocks;
if (Util.SDK_INT >= 16) {
updateFrameworkCryptoInfoV16();
}
}

public void setPattern(int patternBlocksToEncrypt, int patternBlocksToSkip) {
this.patternBlocksToEncrypt = patternBlocksToEncrypt;
this.patternBlocksToSkip = patternBlocksToSkip;
if (Util.SDK_INT >= 24) {
patternHolder.set(patternBlocksToEncrypt, patternBlocksToSkip);
}
}

/**
* Returns an equivalent {@link android.media.MediaCodec.CryptoInfo} instance.
* <p>
Expand Down Expand Up @@ -122,7 +114,7 @@ private void updateFrameworkCryptoInfoV16() {
frameworkCryptoInfo.iv = iv;
frameworkCryptoInfo.mode = mode;
if (Util.SDK_INT >= 24) {
patternHolder.set(patternBlocksToEncrypt, patternBlocksToSkip);
patternHolder.set(encryptedBlocks, clearBlocks);
}
}

Expand All @@ -137,8 +129,8 @@ private PatternHolderV24(android.media.MediaCodec.CryptoInfo frameworkCryptoInfo
pattern = new android.media.MediaCodec.CryptoInfo.Pattern(0, 0);
}

private void set(int blocksToEncrypt, int blocksToSkip) {
pattern.set(blocksToEncrypt, blocksToSkip);
private void set(int encryptedBlocks, int clearBlocks) {
pattern.set(encryptedBlocks, clearBlocks);
frameworkCryptoInfo.setPattern(pattern);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,30 @@ final class CryptoData {
*/
public final byte[] encryptionKey;

public CryptoData(@C.CryptoMode int cryptoMode, byte[] encryptionKey) {
/**
* The number of encrypted blocks in the encryption pattern, 0 if pattern encryption does not
* apply.
*/
public final int encryptedBlocks;

/**
* The number of clear blocks in the encryption pattern, 0 if pattern encryption does not
* apply.
*/
public final int clearBlocks;

/**
* @param cryptoMode See {@link #cryptoMode}.
* @param encryptionKey See {@link #encryptionKey}.
* @param encryptedBlocks See {@link #encryptedBlocks}.
* @param clearBlocks See {@link #clearBlocks}.
*/
public CryptoData(@C.CryptoMode int cryptoMode, byte[] encryptionKey, int encryptedBlocks,
int clearBlocks) {
this.cryptoMode = cryptoMode;
this.encryptionKey = encryptionKey;
this.encryptedBlocks = encryptedBlocks;
this.clearBlocks = clearBlocks;
}

@Override
Expand All @@ -56,13 +77,16 @@ public boolean equals(Object obj) {
return false;
}
CryptoData other = (CryptoData) obj;
return cryptoMode == other.cryptoMode && Arrays.equals(encryptionKey, other.encryptionKey);
return cryptoMode == other.cryptoMode && encryptedBlocks == other.encryptedBlocks
&& clearBlocks == other.clearBlocks && Arrays.equals(encryptionKey, other.encryptionKey);
}

@Override
public int hashCode() {
int result = cryptoMode;
result = 31 * result + Arrays.hashCode(encryptionKey);
result = 31 * result + encryptedBlocks;
result = 31 * result + clearBlocks;
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,8 @@ public int read(ExtractorInput input, PositionHolder seekPosition) throws IOExce
case ID_CONTENT_ENCRYPTION_KEY_ID:
byte[] encryptionKey = new byte[contentSize];
input.readFully(encryptionKey, 0, contentSize);
currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey);
currentTrack.cryptoData = new TrackOutput.CryptoData(C.CRYPTO_MODE_AES_CTR, encryptionKey,
0, 0); // We assume patternless AES-CTR.
break;
case ID_SIMPLE_BLOCK:
case ID_BLOCK:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,13 +1105,30 @@ private static TrackEncryptionBox parseSchiFromParent(ParsableByteArray parent,
int childAtomSize = parent.readInt();
int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_tenc) {
parent.skipBytes(6);
boolean defaultIsEncrypted = parent.readUnsignedByte() == 1;
int defaultInitVectorSize = parent.readUnsignedByte();
int fullAtom = parent.readInt();
int version = Atom.parseFullAtomVersion(fullAtom);
parent.skipBytes(1); // reserved = 0.
int defaultCryptByteBlock = 0;
int defaultSkipByteBlock = 0;
if (version == 0) {
parent.skipBytes(1); // reserved = 0.
} else /* version 1 or greater */ {
int patternByte = parent.readUnsignedByte();
defaultCryptByteBlock = (patternByte & 0xF0) >> 4;
defaultSkipByteBlock = patternByte & 0x0F;
}
boolean defaultIsProtected = parent.readUnsignedByte() == 1;
int defaultPerSampleIvSize = parent.readUnsignedByte();
byte[] defaultKeyId = new byte[16];
parent.readBytes(defaultKeyId, 0, defaultKeyId.length);
return new TrackEncryptionBox(defaultIsEncrypted, schemeType, defaultInitVectorSize,
defaultKeyId);
byte[] constantIv = null;
if (defaultIsProtected && defaultPerSampleIvSize == 0) {
int constantIvSize = parent.readUnsignedByte();
constantIv = new byte[constantIvSize];
parent.readBytes(constantIv, 0, constantIvSize);
}
return new TrackEncryptionBox(defaultIsProtected, schemeType, defaultPerSampleIvSize,
defaultKeyId, defaultCryptByteBlock, defaultSkipByteBlock, constantIv);
}
childPosition += childAtomSize;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public Extractor[] createExtractors() {
private final ParsableByteArray nalPrefix;
private final ParsableByteArray nalBuffer;
private final ParsableByteArray encryptionSignalByte;
private final ParsableByteArray defaultInitializationVector;

// Adjusts sample timestamps.
private final TimestampAdjuster timestampAdjuster;
Expand Down Expand Up @@ -197,6 +198,7 @@ public FragmentedMp4Extractor(@Flags int flags, TimestampAdjuster timestampAdjus
nalPrefix = new ParsableByteArray(5);
nalBuffer = new ParsableByteArray();
encryptionSignalByte = new ParsableByteArray(1);
defaultInitializationVector = new ParsableByteArray();
extendedTypeScratch = new byte[16];
containerAtoms = new Stack<>();
pendingMetadataSampleInfos = new LinkedList<>();
Expand Down Expand Up @@ -879,9 +881,9 @@ private static void parseSgpd(ParsableByteArray sbgp, ParsableByteArray sgpd, St
return;
}
if (Atom.parseFullAtomVersion(sbgpFullAtom) == 1) {
sbgp.skipBytes(4);
sbgp.skipBytes(4); // default_length.
}
if (sbgp.readInt() != 1) {
if (sbgp.readInt() != 1) { // entry_count.
throw new ParserException("Entry count in sbgp != 1 (unsupported).");
}

Expand All @@ -894,25 +896,35 @@ private static void parseSgpd(ParsableByteArray sbgp, ParsableByteArray sgpd, St
int sgpdVersion = Atom.parseFullAtomVersion(sgpdFullAtom);
if (sgpdVersion == 1) {
if (sgpd.readUnsignedInt() == 0) {
throw new ParserException("Variable length decription in sgpd found (unsupported)");
throw new ParserException("Variable length description in sgpd found (unsupported)");
}
} else if (sgpdVersion >= 2) {
sgpd.skipBytes(4);
sgpd.skipBytes(4); // default_sample_description_index.
}
if (sgpd.readUnsignedInt() != 1) {
if (sgpd.readUnsignedInt() != 1) { // entry_count.
throw new ParserException("Entry count in sgpd != 1 (unsupported).");
}
// CencSampleEncryptionInformationGroupEntry
sgpd.skipBytes(2);
sgpd.skipBytes(1); // reserved = 0.
int patternByte = sgpd.readUnsignedByte();
int cryptByteBlock = (patternByte & 0xF0) >> 4;
int skipByteBlock = patternByte & 0x0F;
boolean isProtected = sgpd.readUnsignedByte() == 1;
if (!isProtected) {
return;
}
int initVectorSize = sgpd.readUnsignedByte();
int perSampleIvSize = sgpd.readUnsignedByte();
byte[] keyId = new byte[16];
sgpd.readBytes(keyId, 0, keyId.length);
byte[] constantIv = null;
if (isProtected && perSampleIvSize == 0) {
int constantIvSize = sgpd.readUnsignedByte();
constantIv = new byte[constantIvSize];
sgpd.readBytes(constantIv, 0, constantIvSize);
}
out.definesEncryptionData = true;
out.trackEncryptionBox = new TrackEncryptionBox(isProtected, schemeType, initVectorSize, keyId);
out.trackEncryptionBox = new TrackEncryptionBox(isProtected, schemeType, perSampleIvSize, keyId,
cryptByteBlock, skipByteBlock, constantIv);
}

/**
Expand Down Expand Up @@ -1197,12 +1209,24 @@ private static TrackBundle getNextFragmentRun(SparseArray<TrackBundle> trackBund
*/
private int appendSampleEncryptionData(TrackBundle trackBundle) {
TrackFragment trackFragment = trackBundle.fragment;
ParsableByteArray sampleEncryptionData = trackFragment.sampleEncryptionData;
int sampleDescriptionIndex = trackFragment.header.sampleDescriptionIndex;
TrackEncryptionBox encryptionBox = trackFragment.trackEncryptionBox != null
? trackFragment.trackEncryptionBox
: trackBundle.track.getSampleDescriptionEncryptionBox(sampleDescriptionIndex);
int vectorSize = encryptionBox.initializationVectorSize;

ParsableByteArray initializationVectorData;
int vectorSize;
if (encryptionBox.initializationVectorSize != 0) {
initializationVectorData = trackFragment.sampleEncryptionData;
vectorSize = encryptionBox.initializationVectorSize;
} else {
// The default initialization vector should be used.
byte[] initVectorData = encryptionBox.defaultInitializationVector;
defaultInitializationVector.reset(initVectorData, initVectorData.length);
initializationVectorData = defaultInitializationVector;
vectorSize = initVectorData.length;
}

boolean subsampleEncryption = trackFragment
.sampleHasSubsampleEncryptionTable[trackBundle.currentSampleIndex];

Expand All @@ -1212,20 +1236,20 @@ private int appendSampleEncryptionData(TrackBundle trackBundle) {
TrackOutput output = trackBundle.output;
output.sampleData(encryptionSignalByte, 1);
// Write the vector.
output.sampleData(sampleEncryptionData, vectorSize);
output.sampleData(initializationVectorData, vectorSize);
// If we don't have subsample encryption data, we're done.
if (!subsampleEncryption) {
return 1 + vectorSize;
}
// Write the subsample encryption data.
int subsampleCount = sampleEncryptionData.readUnsignedShort();
sampleEncryptionData.skipBytes(-2);
ParsableByteArray subsampleEncryptionData = trackFragment.sampleEncryptionData;
int subsampleCount = subsampleEncryptionData.readUnsignedShort();
subsampleEncryptionData.skipBytes(-2);
int subsampleDataLength = 2 + 6 * subsampleCount;
output.sampleData(sampleEncryptionData, subsampleDataLength);
output.sampleData(subsampleEncryptionData, subsampleDataLength);
return 1 + vectorSize + subsampleDataLength;
}


/** Returns DrmInitData from leaf atoms. */
private static DrmInitData getDrmInitDataFromAtoms(List<Atom.LeafAtom> leafChildren) {
ArrayList<SchemeData> schemeDatas = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.util.Log;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Assertions;

/**
* Encapsulates information parsed from a track encryption (tenc) box or sample group description
Expand Down Expand Up @@ -49,19 +50,31 @@ public final class TrackEncryptionBox {
*/
public final int initializationVectorSize;

/**
* If {@link #initializationVectorSize} is 0, holds the default initialization vector as defined
* in the track encryption box or sample group description box. Null otherwise.
*/
public final byte[] defaultInitializationVector;

/**
* @param isEncrypted See {@link #isEncrypted}.
* @param schemeType See {@link #schemeType}.
* @param initializationVectorSize See {@link #initializationVectorSize}.
* @param keyId See {@link TrackOutput.CryptoData#encryptionKey}.
* @param defaultEncryptedBlocks See {@link TrackOutput.CryptoData#encryptedBlocks}.
* @param defaultClearBlocks See {@link TrackOutput.CryptoData#clearBlocks}.
* @param defaultInitializationVector See {@link #defaultInitializationVector}.
*/
public TrackEncryptionBox(boolean isEncrypted, @Nullable String schemeType,
int initializationVectorSize, byte[] keyId) {
int initializationVectorSize, byte[] keyId, int defaultEncryptedBlocks,
int defaultClearBlocks, @Nullable byte[] defaultInitializationVector) {
Assertions.checkArgument(initializationVectorSize == 0 ^ defaultInitializationVector == null);
this.isEncrypted = isEncrypted;
this.schemeType = schemeType;
this.initializationVectorSize = initializationVectorSize;
cryptoData = new TrackOutput.CryptoData(schemeToCryptoMode(schemeType), keyId);
this.defaultInitializationVector = defaultInitializationVector;
cryptoData = new TrackOutput.CryptoData(schemeToCryptoMode(schemeType), keyId,
defaultEncryptedBlocks, defaultClearBlocks);
}

@C.CryptoMode
Expand All @@ -72,8 +85,10 @@ private static int schemeToCryptoMode(@Nullable String schemeType) {
}
switch (schemeType) {
case "cenc":
case "cens":
return C.CRYPTO_MODE_AES_CTR;
case "cbc1":
case "cbcs":
return C.CRYPTO_MODE_AES_CBC;
default:
Log.w(TAG, "Unsupported protection scheme type '" + schemeType + "'. Assuming AES-CTR "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,8 @@ private void readEncryptionData(DecoderInputBuffer buffer, SampleExtrasHolder ex
// Populate the cryptoInfo.
CryptoData cryptoData = extrasHolder.cryptoData;
buffer.cryptoInfo.set(subsampleCount, clearDataSizes, encryptedDataSizes,
cryptoData.encryptionKey, buffer.cryptoInfo.iv, cryptoData.cryptoMode);
cryptoData.encryptionKey, buffer.cryptoInfo.iv, cryptoData.cryptoMode,
cryptoData.encryptedBlocks, cryptoData.clearBlocks);

// Adjust the offset and size to take into account the bytes read.
int bytesRead = (int) (offset - extrasHolder.offset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public SsMediaPeriod(SsManifest manifest, SsChunkSource.Factory chunkSourceFacto
ProtectionElement protectionElement = manifest.protectionElement;
if (protectionElement != null) {
byte[] keyId = getProtectionElementKeyId(protectionElement.data);
// We assume pattern encryption does not apply.
trackEncryptionBoxes = new TrackEncryptionBox[] {
new TrackEncryptionBox(true, null, INITIALIZATION_VECTOR_SIZE, keyId)};
new TrackEncryptionBox(true, null, INITIALIZATION_VECTOR_SIZE, keyId, 0, 0, null)};
} else {
trackEncryptionBoxes = null;
}
Expand Down

0 comments on commit aca80b8

Please sign in to comment.