diff --git a/README.md b/README.md index 7edb97be4..ee53f1d06 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ Microsoft Azure tables, blobs, queues, service bus (queues and topics), service * Encoding / process asset, create job, job templates * Manage media services entities: create / update / read / delete / get list * Delivery SAS and Streaming media content - * Dynamic encryption: AES and DRM (PlayReady/Widevine) with and without Token restriction + * Dynamic encryption: AES and DRM (PlayReady/Widevine/FairPlay) with and without Token restriction * Scale encoding reserved unit type - * REST API Version: 2.11 + * REST API Version: 2.12 # Getting Started diff --git a/examples/MediaServices/README.md b/examples/MediaServices/README.md index d5d65796a..748ff10a8 100644 --- a/examples/MediaServices/README.md +++ b/examples/MediaServices/README.md @@ -1,7 +1,9 @@ This folder contains the following Azure Media Service PHP SDK samples: +* userconfig.php: common file to store the AMS credentials to execute the samples. * vodworkflow_aes.php: End-to-end VOD workflow that applies AES content protection. -* vodworkflow_aes.php: End-to-end VOD workflow that applies DRM (PlayReady + Widevine) content protection. +* vodworkflow_drm_playready_widevine.php: End-to-end VOD workflow that applies DRM (PlayReady + Widevine) content protection. +* vodworkflow_drm_fairplay.php: End-to-end VOD workflow that applies DRM (FairPlay) content protection. * scale_encoding_units.php: Scales the encoding reserved units. To run these samples you can use the following command (assuming that your include_path are correctly configured) diff --git a/examples/MediaServices/vodworkflow_aes.php b/examples/MediaServices/vodworkflow_aes.php index f839ef74a..d8424b703 100644 --- a/examples/MediaServices/vodworkflow_aes.php +++ b/examples/MediaServices/vodworkflow_aes.php @@ -45,9 +45,9 @@ use WindowsAzure\MediaServices\Models\AssetDeliveryProtocol; use WindowsAzure\MediaServices\Models\AssetDeliveryPolicyType; use WindowsAzure\MediaServices\Models\AssetDeliveryPolicyConfigurationKey; +use WindowsAzure\MediaServices\Templates\SymmetricVerificationKey; use WindowsAzure\MediaServices\Templates\TokenRestrictionTemplateSerializer; use WindowsAzure\MediaServices\Templates\TokenRestrictionTemplate; -use WindowsAzure\MediaServices\Templates\SymmetricVerificationKey; use WindowsAzure\MediaServices\Templates\TokenClaim; use WindowsAzure\MediaServices\Templates\TokenType; @@ -106,7 +106,7 @@ function uploadFileAndCreateAsset($restProxy, $mezzanineFileName) $asset = $restProxy->createAsset($asset); $assetId = $asset->getId(); - echo 'Asset created: name='.$asset->getName().' id='.$assetId."\r\n"; + echo "Asset created: name={$asset->getName()} id={$assetId}\r\n"; // 1.3. create an Access Policy with Write permissions $accessPolicy = new AccessPolicy('UploadAccessPolicy'); @@ -284,7 +284,7 @@ function createAssetDeliveryPolicy($restProxy, $encodedAsset, $contentKey) // 5.2 Generate the AssetDeliveryPolicy Configuration Key $randomKey = Utilities::generateCryptoKey(16); $configuration = [AssetDeliveryPolicyConfigurationKey::ENVELOPE_KEY_ACQUISITION_URL => $acquisitionUrl, - AssetDeliveryPolicyConfigurationKey::ENVELOPE_ENCRYPTION_IV_AS_BASE64 => base64_encode($randomKey), ]; + AssetDeliveryPolicyConfigurationKey::ENVELOPE_ENCRYPTION_IV_AS_BASE64 => base64_encode($randomKey)]; $confJson = AssetDeliveryPolicyConfigurationKey::stringifyAssetDeliveryPolicyConfiguartionKey($configuration); // 5.3 Create the AssetDeliveryPolicy @@ -369,3 +369,5 @@ function endsWith($haystack, $needle) return substr($haystack, -$length) === $needle; } + +?> diff --git a/examples/MediaServices/vodworkflow_drm_fairplay.php b/examples/MediaServices/vodworkflow_drm_fairplay.php new file mode 100644 index 000000000..605939531 --- /dev/null +++ b/examples/MediaServices/vodworkflow_drm_fairplay.php @@ -0,0 +1,464 @@ + + * @copyright 2012 Microsoft Corporation + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://github.com/windowsazure/azure-sdk-for-php + */ + +require_once __DIR__.'/../vendor/autoload.php'; + +use WindowsAzure\Common\ServicesBuilder; +use WindowsAzure\Common\Internal\MediaServicesSettings; +use WindowsAzure\Common\Internal\Utilities; +use WindowsAzure\MediaServices\Models\Asset; +use WindowsAzure\MediaServices\Models\AccessPolicy; +use WindowsAzure\MediaServices\Models\Locator; +use WindowsAzure\MediaServices\Models\Task; +use WindowsAzure\MediaServices\Models\Job; +use WindowsAzure\MediaServices\Models\TaskOptions; +use WindowsAzure\MediaServices\Models\ContentKey; +use WindowsAzure\MediaServices\Models\ProtectionKeyTypes; +use WindowsAzure\MediaServices\Models\ContentKeyTypes; +use WindowsAzure\MediaServices\Models\ContentKeyAuthorizationPolicy; +use WindowsAzure\MediaServices\Models\ContentKeyAuthorizationPolicyOption; +use WindowsAzure\MediaServices\Models\ContentKeyAuthorizationPolicyRestriction; +use WindowsAzure\MediaServices\Models\ContentKeyDeliveryType; +use WindowsAzure\MediaServices\Models\ContentKeyRestrictionType; +use WindowsAzure\MediaServices\Models\AssetDeliveryPolicy; +use WindowsAzure\MediaServices\Models\AssetDeliveryProtocol; +use WindowsAzure\MediaServices\Models\AssetDeliveryPolicyType; +use WindowsAzure\MediaServices\Models\AssetDeliveryPolicyConfigurationKey; +use WindowsAzure\MediaServices\Templates\SymmetricVerificationKey; +use WindowsAzure\MediaServices\Templates\TokenRestrictionTemplateSerializer; +use WindowsAzure\MediaServices\Templates\TokenRestrictionTemplate; +use WindowsAzure\MediaServices\Templates\TokenClaim; +use WindowsAzure\MediaServices\Templates\TokenType; +use WindowsAzure\MediaServices\Templates\FairPlayConfiguration; + +// read user settings from config +include_once 'userconfig.php'; + +$mezzanineFileName = __DIR__.'/Azure-Video.wmv'; +$tokenRestriction = true; +$tokenType = TokenType::JWT; + +// FairPlay +$fairPlayASK = ''; +$fairPlayPFXFile = ''; +$fairPlayPFXPassword = ''; +$fairPlayIV = bin2hex(openssl_random_pseudo_bytes(16)); + +echo "Azure SDK for PHP - FairPlay Dynamic Encryption Sample\r\n"; + +// 0 - set up the MediaServicesService object to call into the Media Services REST API. +$restProxy = ServicesBuilder::getInstance()->createMediaServicesService(new MediaServicesSettings($account, $secret)); + +// 1 - Upload the mezzanine +$sourceAsset = uploadFileAndCreateAsset($restProxy, $mezzanineFileName); + +// 2 - encode the output asset +$encodedAsset = encodeToAdaptiveBitrateMP4Set($restProxy, $sourceAsset); + +// 3 - Create Content Key +$contentKey = createCommonCBCTypeContentKey($restProxy, $encodedAsset); + +// 4 - Create the ContentKey Authorization Policy +$tokenTemplateString = null; +if ($tokenRestriction) { + $tokenTemplateString = addTokenRestrictedAuthorizationPolicy($restProxy, $contentKey, $tokenType, $fairPlayASK, $fairPlayPFXPassword, $fairPlayPFXFile, $fairPlayIV); +} else { + addOpenAuthorizationPolicy($restProxy, $contentKey, $fairPlayASK, $fairPlayPFXPassword, $fairPlayPFXFile, $fairPlayIV); +} + +// 5 - Create the AssetDeliveryPolicy +createAssetDeliveryPolicy($restProxy, $encodedAsset, $contentKey, $fairPlayIV); + +// 6 - Publish +publishEncodedAsset($restProxy, $encodedAsset); + +// 7 - Generate Test Token +if ($tokenRestriction) { + generateTestToken($tokenTemplateString, $contentKey); +} + +// Done +echo "Done!"; + +//////////////////// +// Helper methods // +//////////////////// + +function uploadFileAndCreateAsset($restProxy, $mezzanineFileName) +{ + // 1.1. create an empty "Asset" by specifying the name + $asset = new Asset(Asset::OPTIONS_NONE); + $asset->setName('Mezzanine '.basename($mezzanineFileName)); + $asset = $restProxy->createAsset($asset); + $assetId = $asset->getId(); + + echo "Asset created: name={$asset->getName()} id={$assetId}\r\n"; + + // 1.3. create an Access Policy with Write permissions + $accessPolicy = new AccessPolicy('UploadAccessPolicy'); + $accessPolicy->setDurationInMinutes(60.0); + $accessPolicy->setPermissions(AccessPolicy::PERMISSIONS_WRITE); + $accessPolicy = $restProxy->createAccessPolicy($accessPolicy); + + // 1.4. create a SAS Locator for the Asset + $sasLocator = new Locator($asset, $accessPolicy, Locator::TYPE_SAS); + $sasLocator->setStartTime(new \DateTime('now -5 minutes')); + $sasLocator = $restProxy->createLocator($sasLocator); + + // 1.5. get the mezzanine file content + $fileContent = file_get_contents($mezzanineFileName); + + echo "Uploading...\r\n"; + + // 1.6. use the 'uploadAssetFile' to perform a multi-part upload using the Block Blobs REST API storage operations + $restProxy->uploadAssetFile($sasLocator, basename($mezzanineFileName), $fileContent); + + // 1.7. notify Media Services that the file upload operation is done to generate the asset file metadata + $restProxy->createFileInfos($asset); + + echo "File uploaded: size=" . strlen($fileContent) . "\r\n"; + + // 1.8. delete the SAS Locator (and Access Policy) for the Asset since we are done uploading files + $restProxy->deleteLocator($sasLocator); + $restProxy->deleteAccessPolicy($accessPolicy); + return $asset; +} + +function encodeToAdaptiveBitrateMP4Set($restProxy, $asset) +{ + // 2.1 retrieve the latest 'Media Encoder Standard' processor version + $mediaProcessor = $restProxy->getLatestMediaProcessor('Media Encoder Standard'); + + echo "Using Media Processor: {$mediaProcessor->getName()} version {$mediaProcessor->getVersion()}\r\n"; + + // 2.2 Create the Job; this automatically schedules and runs it + $outputAssetName = "Encoded " . $asset->getName(); + $outputAssetCreationOption = Asset::OPTIONS_NONE; + $taskBody = 'JobInputAsset(0)JobOutputAsset(0)'; + + $task = new Task($taskBody, $mediaProcessor->getId(), TaskOptions::NONE); + $task->setConfiguration('H264 Multiple Bitrate 720p'); + + $job = new Job(); + $job->setName('Encoding Job'); + + $job = $restProxy->createJob($job, array($asset), array($task)); + + echo "Created Job with Id: {$job->getId()}\r\n"; + + // 2.3 Check to see if the Job has completed + $result = $restProxy->getJobStatus($job); + + $jobStatusMap = array('Queued', 'Scheduled', 'Processing', 'Finished', 'Error', 'Canceled', 'Canceling'); + + while($result != Job::STATE_FINISHED && $result != Job::STATE_ERROR && $result != Job::STATE_CANCELED) { + echo "Job status: {$jobStatusMap[$result]}\r\n"; + sleep(5); + $result = $restProxy->getJobStatus($job); + } + + if ($result != Job::STATE_FINISHED) { + echo "The job has finished with a wrong status: {$jobStatusMap[$result]}\r\n"; + exit(-1); + } + + echo "Job Finished!\r\n"; + + // 2.4 Get output asset + $outputAssets = $restProxy->getJobOutputMediaAssets($job); + $encodedAsset = $outputAssets[0]; + + echo "Asset encoded: name={$encodedAsset->getName()} id={$encodedAsset->getId()}\r\n"; + + return $encodedAsset; +} + +function createCommonCBCTypeContentKey($restProxy, $encodedAsset) +{ + // 3.1 Generate a new key + $aesKey = Utilities::generateCryptoKey(16); + + // 3.2 Get the protection key id for ContentKey + $protectionKeyId = $restProxy->getProtectionKeyId(ContentKeyTypes::COMMON_ENCRYPTION_CBCS); + $protectionKey = $restProxy->getProtectionKey($protectionKeyId); + + $contentKey = new ContentKey(); + $contentKey->setContentKey($aesKey, $protectionKey); + $contentKey->setProtectionKeyId($protectionKeyId); + $contentKey->setProtectionKeyType(ProtectionKeyTypes::X509_CERTIFICATE_THUMBPRINT); + $contentKey->setContentKeyType(ContentKeyTypes::COMMON_ENCRYPTION_CBCS); + + // 3.3 Create the ContentKey + $contentKey = $restProxy->createContentKey($contentKey); + + echo "Content Key id={$contentKey->getId()}\r\n"; + + // 3.4 Associate the ContentKey with the Asset + $restProxy->linkContentKeyToAsset($encodedAsset, $contentKey); + + return $contentKey; +} + +function addOpenAuthorizationPolicy($restProxy, $contentKey, $fairPlayASK, $fairPlayPfxPassword, $fairPlayPfxFile, $fairPlayIV) +{ + // 4.1 Create ContentKeyAuthorizationPolicyRestriction (Open) + $restriction = new ContentKeyAuthorizationPolicyRestriction(); + $restriction->setName('ContentKey Authorization Policy Restriction'); + $restriction->setKeyRestrictionType(ContentKeyRestrictionType::OPEN); + + // 4.2 Configure FairPlay license options. + $fairPlayConfiguration = configureFairPlayPolicyOptions($restProxy, $fairPlayASK, $fairPlayPfxPassword, $fairPlayPfxFile, $fairPlayIV); + + // 4.3 Create ContentKeyAuthorizationPolicyOption (FairPlay) + $fairPlayOption = new ContentKeyAuthorizationPolicyOption(); + $fairPlayOption->setName('Deliver Common CBC Content Key with open restrictions'); + $fairPlayOption->setKeyDeliveryType(ContentKeyDeliveryType::FAIRPLAY); + $fairPlayOption->setRestrictions(array($restriction)); + $fairPlayOption->setKeyDeliveryConfiguration($fairPlayConfiguration); + $fairPlayOption = $restProxy->createContentKeyAuthorizationPolicyOption($fairPlayOption); + + // 4.4 Create ContentKeyAuthorizationPolicy + $ckapolicy = new ContentKeyAuthorizationPolicy(); + $ckapolicy->setName('ContentKey Authorization Policy'); + $ckapolicy = $restProxy->createContentKeyAuthorizationPolicy($ckapolicy); + + // 4.5 Link the ContentKeyAuthorizationPolicyOption to the ContentKeyAuthorizationPolicy + $restProxy->linkOptionToContentKeyAuthorizationPolicy($fairPlayOption, $ckapolicy); + + // 4.6 Associate the ContentKeyAuthorizationPolicy with the ContentKey + $contentKey->setAuthorizationPolicyId($ckapolicy->getId()); + $restProxy->updateContentKey($contentKey); + + echo "Added Content Key Authorization Policy: name={$ckapolicy->getName()} id={$ckapolicy->getId()}\r\n"; +} + +function addTokenRestrictedAuthorizationPolicy($restProxy, $contentKey, $tokenType, $fairPlayASK, $fairPlayPfxPassword, $fairPlayPfxFile, $fairPlayIV) +{ + // 4.1 Create ContentKeyAuthorizationPolicyRestriction (Token Restricted) + $tokenRestriction = generateTokenRequirements($tokenType); + $restriction = new ContentKeyAuthorizationPolicyRestriction(); + $restriction->setName('ContentKey Authorization Policy Restriction'); + $restriction->setKeyRestrictionType(ContentKeyRestrictionType::TOKEN_RESTRICTED); + $restriction->setRequirements($tokenRestriction); + + // 4.2 Configure FairPlay license options. + $fairPlayConfiguration = configureFairPlayPolicyOptions($restProxy, $fairPlayASK, $fairPlayPfxPassword, $fairPlayPfxFile, $fairPlayIV); + + // 4.3 Create ContentKeyAuthorizationPolicyOption (FairPlay) + $fairPlayOption = new ContentKeyAuthorizationPolicyOption(); + $fairPlayOption->setName('Deliver Common CBC Content Key with token restrictions'); + $fairPlayOption->setKeyDeliveryType(ContentKeyDeliveryType::FAIRPLAY); + $fairPlayOption->setRestrictions(array($restriction)); + $fairPlayOption->setKeyDeliveryConfiguration($fairPlayConfiguration); + $fairPlayOption = $restProxy->createContentKeyAuthorizationPolicyOption($fairPlayOption); + + // 4.4 Create ContentKeyAuthorizationPolicy + $ckapolicy = new ContentKeyAuthorizationPolicy(); + $ckapolicy->setName('ContentKey Authorization Policy'); + $ckapolicy = $restProxy->createContentKeyAuthorizationPolicy($ckapolicy); + + // 4.5 Link the ContentKeyAuthorizationPolicyOption to the ContentKeyAuthorizationPolicy + $restProxy->linkOptionToContentKeyAuthorizationPolicy($fairPlayOption, $ckapolicy); + + // 4.6 Associate the ContentKeyAuthorizationPolicy with the ContentKey + $contentKey->setAuthorizationPolicyId($ckapolicy->getId()); + $restProxy->updateContentKey($contentKey); + + echo "Added Content Key Authorization Policy: name={$ckapolicy->getName()} id={$ckapolicy->getId()}\r\n"; + return $tokenRestriction; +} + +function createAssetDeliveryPolicy($restProxy, $encodedAsset, $contentKey, $fairPlayIV) +{ + // 5.1 Get the acquisition URL + $acquisitionUrl = $restProxy->getKeyDeliveryUrl($contentKey, ContentKeyDeliveryType::FAIRPLAY); + + $acquisitionUrl = str_replace_first("https", "skd", $acquisitionUrl); + + // remove query string + if (strpos($acquisitionUrl, '?') !== false) { + $acquisitionUrl = substr($acquisitionUrl, 0, strrpos($acquisitionUrl, "?")); + } + + // 5.2 Generate the AssetDeliveryPolicy Configuration Key + $configuration = [AssetDeliveryPolicyConfigurationKey::FAIRPLAY_BASE_LICENSE_ACQUISITION_URL => $acquisitionUrl, + AssetDeliveryPolicyConfigurationKey::COMMON_ENCRYPTION_IV_FOR_CBCS => $fairPlayIV]; + $confJson = AssetDeliveryPolicyConfigurationKey::stringifyAssetDeliveryPolicyConfiguartionKey($configuration); + + // 5.3 Create the AssetDeliveryPolicy + $adpolicy = new AssetDeliveryPolicy(); + $adpolicy->setName('Asset Delivery Policy'); + $adpolicy->setAssetDeliveryProtocol(AssetDeliveryProtocol::HLS); + $adpolicy->setAssetDeliveryPolicyType(AssetDeliveryPolicyType::DYNAMIC_COMMON_ENCRYPTION_CBCS); + $adpolicy->setAssetDeliveryConfiguration($confJson); + + $adpolicy = $restProxy->createAssetDeliveryPolicy($adpolicy); + + // 5.4 Link the AssetDeliveryPolicy to the Asset + $restProxy->linkDeliveryPolicyToAsset($encodedAsset, $adpolicy->getId()); + + echo "Added Asset Delivery Policy: name={$adpolicy->getName()} id={$adpolicy->getId()}\r\n"; +} + +function publishEncodedAsset($restProxy, $encodedAsset) +{ + // 6.1 Get the .ISM AssetFile + $files = $restProxy->getAssetAssetFileList($encodedAsset); + $manifestFile = null; + + foreach($files as $file) { + if (endsWith(strtolower($file->getName()), '.ism')) { + $manifestFile = $file; + } + } + + if ($manifestFile == null) { + echo "Unable to found the manifest file\r\n"; + exit(-1); + } + + // 6.2 Create a 30-day read-only AccessPolicy + $access = new AccessPolicy("Streaming Access Policy"); + $access->setDurationInMinutes(60 * 24 * 30); + $access->setPermissions(AccessPolicy::PERMISSIONS_READ); + $access = $restProxy->createAccessPolicy($access); + + // 6.3 Create a Locator using the AccessPolicy and Asset + $locator = new Locator($encodedAsset, $access, Locator::TYPE_ON_DEMAND_ORIGIN); + $locator->setName("Streaming Locator"); + $locator = $restProxy->createLocator($locator); + + // 6.4 Create a Smooth Streaming base URL + $stremingUrl = $locator->getPath() . $manifestFile->getName() . "/manifest(format=m3u8-aapl)"; + + echo "Streaming URL: {$stremingUrl}\r\n"; +} + +function configureFairPlayPolicyOptions($restProxy, $fairPlayASK, $fairPlayPfxPassword, $fairPlayPfxFile, $fairPlayIV) +{ + + $askContentKey = createFairPlayAskTypeContentKey($restProxy, $fairPlayASK); + + $pfxPasswordContentKey = createFairPlayPfxPasswordTypeContentKey($restProxy, $fairPlayPfxPassword); + + // open the pfx file + + $strAskContentKey = substr($askContentKey->getId(), strlen("nb:kid:UUID:")); + $strPassContentKey = substr($pfxPasswordContentKey->getId(), strlen("nb:kid:UUID:")); + + $certData = file_get_contents($fairPlayPfxFile); + openssl_pkcs12_read($certData, $certsOut, $fairPlayPfxPassword); + + return FairPlayConfiguration::createSerializedFairPlayOptionConfiguration($certsOut["cert"], $certsOut["pkey"], + $fairPlayPfxPassword, $strPassContentKey, $strAskContentKey, $fairPlayIV); +} + +function createFairPlayAskTypeContentKey($restProxy, $fairPlayASK) +{ + // 3.1 Convert the ASK to binary representation + $askKey = hex2bin($fairPlayASK); + + // 3.2 Get the protection key id for ContentKey + $protectionKeyId = $restProxy->getProtectionKeyId(ContentKeyTypes::FAIRPLAY_ASK); + $protectionKey = $restProxy->getProtectionKey($protectionKeyId); + + $contentKey = new ContentKey(); + $contentKey->setContentKey($askKey, $protectionKey, TRUE); + $contentKey->setProtectionKeyId($protectionKeyId); + $contentKey->setProtectionKeyType(ProtectionKeyTypes::X509_CERTIFICATE_THUMBPRINT); + $contentKey->setContentKeyType(ContentKeyTypes::FAIRPLAY_ASK); + + // 3.3 Create the ContentKey + $contentKey = $restProxy->createContentKey($contentKey); + + echo "FairPlay ASK Content Key id={$contentKey->getId()}\r\n"; + + return $contentKey; +} + +function createFairPlayPfxPasswordTypeContentKey($restProxy, $fairPlayPfxPassword) +{ + // 3.1 Get the protection key id for ContentKey + $protectionKeyId = $restProxy->getProtectionKeyId(ContentKeyTypes::FAIRPLAY_PFXPASSWORD); + $protectionKey = $restProxy->getProtectionKey($protectionKeyId); + + // 3.2 Prepare the content key + $contentKey = new ContentKey(); + $contentKey->setContentKey($fairPlayPfxPassword, $protectionKey, TRUE); + $contentKey->setProtectionKeyId($protectionKeyId); + $contentKey->setProtectionKeyType(ProtectionKeyTypes::X509_CERTIFICATE_THUMBPRINT); + $contentKey->setContentKeyType(ContentKeyTypes::FAIRPLAY_PFXPASSWORD); + + // 3.3 Create the ContentKey + $contentKey = $restProxy->createContentKey($contentKey); + + echo "FairPlay PFX Password Content Key id={$contentKey->getId()}\r\n"; + + return $contentKey; +} + +function generateTokenRequirements($tokenType) +{ + $template = new TokenRestrictionTemplate($tokenType); + + $template->setPrimaryVerificationKey(new SymmetricVerificationKey()); + $template->setAudience("urn:contoso"); + $template->setIssuer("https://sts.contoso.com"); + $claims = array(); + $claims[] = new TokenClaim(TokenClaim::CONTENT_KEY_ID_CLAIM_TYPE); + $template->setRequiredClaims($claims); + + return TokenRestrictionTemplateSerializer::serialize($template); +} + +function generateTestToken($tokenTemplateString, $contentKey) +{ + $template = TokenRestrictionTemplateSerializer::deserialize($tokenTemplateString); + $contentKeyUUID = substr($contentKey->getId(), strlen("nb:kid:UUID:")); + $expiration = strtotime("+12 hour"); + $token = TokenRestrictionTemplateSerializer::generateTestToken($template, null, $contentKeyUUID, $expiration); + + echo "Token Type {$template->getTokenType()}\r\nBearer={$token}\r\n"; +} + +function endsWith($haystack, $needle) +{ + $length = strlen($needle); + if ($length == 0) { + return true; + } + + return (substr($haystack, -$length) === $needle); +} + +function str_replace_first($search, $replace, $subject) +{ + $pos = strpos($subject, $search); + if ($pos !== false) { + return substr_replace($subject, $replace, $pos, strlen($search)); + } + return $subject; +} + +?> diff --git a/examples/MediaServices/vodworkflow_drm_playready_widevine.php b/examples/MediaServices/vodworkflow_drm_playready_widevine.php index 92c4770b1..8f5eb9cde 100644 --- a/examples/MediaServices/vodworkflow_drm_playready_widevine.php +++ b/examples/MediaServices/vodworkflow_drm_playready_widevine.php @@ -54,9 +54,9 @@ use WindowsAzure\MediaServices\Templates\ContentKeySpecs; use WindowsAzure\MediaServices\Templates\RequiredOutputProtection; use WindowsAzure\MediaServices\Templates\Hdcp; +use WindowsAzure\MediaServices\Templates\SymmetricVerificationKey; use WindowsAzure\MediaServices\Templates\TokenRestrictionTemplateSerializer; use WindowsAzure\MediaServices\Templates\TokenRestrictionTemplate; -use WindowsAzure\MediaServices\Templates\SymmetricVerificationKey; use WindowsAzure\MediaServices\Templates\TokenClaim; use WindowsAzure\MediaServices\Templates\TokenType; use WindowsAzure\MediaServices\Templates\WidevineMessageSerializer; @@ -115,7 +115,7 @@ function uploadFileAndCreateAsset($restProxy, $mezzanineFileName) $asset = $restProxy->createAsset($asset); $assetId = $asset->getId(); - echo 'Asset created: name='.$asset->getName().' id='.$assetId."\r\n"; + echo "Asset created: name={$asset->getName()} id={$assetId}\r\n"; // 1.3. create an Access Policy with Write permissions $accessPolicy = new AccessPolicy('UploadAccessPolicy'); @@ -317,11 +317,16 @@ function createAssetDeliveryPolicy($restProxy, $encodedAsset, $contentKey) { // 5.1 Get the acquisition URL $acquisitionUrl = $restProxy->getKeyDeliveryUrl($contentKey, ContentKeyDeliveryType::PLAYREADY_LICENSE); - $widevineURl = $restProxy->getKeyDeliveryUrl($contentKey, ContentKeyDeliveryType::WIDEVINE); + $widevineUrl = $restProxy->getKeyDeliveryUrl($contentKey, ContentKeyDeliveryType::WIDEVINE); + + // remove query string + if (strpos($widevineUrl, '?') !== false) { + $widevineUrl = substr($widevineUrl, 0, strrpos($widevineUrl, "?")); + } // 5.2 Generate the AssetDeliveryPolicy Configuration Key $configuration = [AssetDeliveryPolicyConfigurationKey::PLAYREADY_LICENSE_ACQUISITION_URL => $acquisitionUrl, - AssetDeliveryPolicyConfigurationKey::WIDEVINE_LICENSE_ACQUISITION_URL => $widevineURl, ]; + AssetDeliveryPolicyConfigurationKey::WIDEVINE_BASE_LICENSE_ACQUISITION_URL => $widevineUrl]; $confJson = AssetDeliveryPolicyConfigurationKey::stringifyAssetDeliveryPolicyConfiguartionKey($configuration); // 5.3 Create the AssetDeliveryPolicy @@ -455,7 +460,6 @@ function generateTokenRequirements($tokenType) function generateTestToken($tokenTemplateString, $contentKey) { - $template = TokenRestrictionTemplateSerializer::deserialize($tokenTemplateString); $contentKeyUUID = substr($contentKey->getId(), strlen('nb:kid:UUID:')); $expiration = strtotime('+12 hour'); $token = TokenRestrictionTemplateSerializer::generateTestToken($template, null, $contentKeyUUID, $expiration); @@ -472,3 +476,5 @@ function endsWith($haystack, $needle) return substr($haystack, -$length) === $needle; } + +?> diff --git a/src/Common/Internal/Resources.php b/src/Common/Internal/Resources.php index d64aa1ed5..07eaac8ae 100644 --- a/src/Common/Internal/Resources.php +++ b/src/Common/Internal/Resources.php @@ -260,7 +260,7 @@ class Resources const ATOM_FEED_CONTENT_TYPE = 'application/atom+xml;type=feed;charset=utf-8'; const ACCEPT_CHARSET_VALUE = 'utf-8'; const INT32_MAX = 2147483647; - const MEDIA_SERVICES_API_LATEST_VERSION = '2.11'; + const MEDIA_SERVICES_API_LATEST_VERSION = '2.12'; const MEDIA_SERVICES_DATA_SERVICE_VERSION_VALUE = '3.0;NetFx'; const MEDIA_SERVICES_MAX_DATA_SERVICE_VERSION_VALUE = '3.0;NetFx'; diff --git a/src/MediaServices/Models/AssetDeliveryPolicyConfigurationKey.php b/src/MediaServices/Models/AssetDeliveryPolicyConfigurationKey.php index 75d461e00..ba42f9243 100644 --- a/src/MediaServices/Models/AssetDeliveryPolicyConfigurationKey.php +++ b/src/MediaServices/Models/AssetDeliveryPolicyConfigurationKey.php @@ -96,6 +96,35 @@ class AssetDeliveryPolicyConfigurationKey */ const WIDEVINE_LICENSE_ACQUISITION_URL = 7; + /** + * WidevineBaseLicenseAcquisitionUrl, Base Widevine url that will have KID= appended + * + * @var int + */ + const WIDEVINE_BASE_LICENSE_ACQUISITION_URL = 8; + + /** + * FairPlay license acquisition URL. + * + * @var int + */ + const FAIRPLAY_LICENSE_ACQUISITION_URL = 9; + + /** + * Base FairPlay license acquisition URL that will have KID= appended. + * + * @var int + */ + const FAIRPLAY_BASE_LICENSE_ACQUISITION_URL = 10; + + /** + * Initialization Vector that will be used for encrypting the content. Must match + * IV in the AssetDeliveryPolicy. + * + * @var int + */ + const COMMON_ENCRYPTION_IV_FOR_CBCS = 11; + /** * Helper function to stringnify the AssetDeliveryPolicyConfigurationKey. * diff --git a/src/MediaServices/Models/AssetDeliveryPolicyType.php b/src/MediaServices/Models/AssetDeliveryPolicyType.php index 93bd254f8..f53cbeb24 100644 --- a/src/MediaServices/Models/AssetDeliveryPolicyType.php +++ b/src/MediaServices/Models/AssetDeliveryPolicyType.php @@ -74,4 +74,12 @@ class AssetDeliveryPolicyType * @var int */ const DYNAMIC_COMMON_ENCRYPTION = 4; + + /** + * Apply Dynamic Common encryption with cbcs + * + * @var int + */ + const DYNAMIC_COMMON_ENCRYPTION_CBCS = 5; + } diff --git a/src/MediaServices/Models/ContentKey.php b/src/MediaServices/Models/ContentKey.php index 8dbd6d37a..687adf80f 100644 --- a/src/MediaServices/Models/ContentKey.php +++ b/src/MediaServices/Models/ContentKey.php @@ -417,8 +417,12 @@ private function _generateEncryptedContentKey($aesKey, $protectionKey) * * @return none */ - private function _generateChecksum($aesKey) + private function _generateChecksum($aesKey, $usePadding = FALSE) { + if ($usePadding) { + $aesKey = $this->pkcs5_pad($aesKey, 16); + } + $encrypted = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, // AES $aesKey, @@ -429,20 +433,29 @@ private function _generateChecksum($aesKey) $this->_checksum = base64_encode(substr($encrypted, 0, 8)); } + /** + * checksum padding + */ + private function pkcs5_pad ($text, $blocksize) { + $pad = $blocksize - (strlen($text) % $blocksize); + return $text . str_repeat(chr($pad), $pad); + } + /** * Set not encrypted content key. Automatically encrypted content key and * set checksum. * * @param string $value Content key * @param string $protectionKey Protection key (public key) from WAMS + * @param boolean $usePadding Set to true to automatically use padding while is generating the checksum * * @return none */ - public function setContentKey($value, $protectionKey) + public function setContentKey($value, $protectionKey, $usePadding = FALSE) { $this->_generateEncryptedContentKey($value, $protectionKey); - $this->_generateChecksum($value); + $this->_generateChecksum($value, $usePadding); } /** diff --git a/src/MediaServices/Models/ContentKeyDeliveryType.php b/src/MediaServices/Models/ContentKeyDeliveryType.php index d4c15de51..228edf385 100644 --- a/src/MediaServices/Models/ContentKeyDeliveryType.php +++ b/src/MediaServices/Models/ContentKeyDeliveryType.php @@ -67,4 +67,12 @@ class ContentKeyDeliveryType * @var int */ const WIDEVINE = 3; + + /** + * Send FairPlay SPC to Key Delivery server and get CKC back + * + * @var int + */ + const FAIRPLAY = 4; + } diff --git a/src/MediaServices/Models/ContentKeyTypes.php b/src/MediaServices/Models/ContentKeyTypes.php index c99c44b6d..7add9de3e 100644 --- a/src/MediaServices/Models/ContentKeyTypes.php +++ b/src/MediaServices/Models/ContentKeyTypes.php @@ -67,4 +67,26 @@ class ContentKeyTypes * @var int */ const ENVELOPE_ENCRYPTION = 4; + + /** + * Specifies a content key for common encryption with CBCS. + * + * @var int + */ + const COMMON_ENCRYPTION_CBCS = 6; + + /** + * Application Secret key for FairPlay. + * + * @var int + */ + const FAIRPLAY_ASK = 7; + + /** + * Password for FairPlay application certificate. + * + * @var int + */ + const FAIRPLAY_PFXPASSWORD = 8; + } diff --git a/src/MediaServices/Templates/FairPlayConfiguration.php b/src/MediaServices/Templates/FairPlayConfiguration.php new file mode 100644 index 000000000..88e68b703 --- /dev/null +++ b/src/MediaServices/Templates/FairPlayConfiguration.php @@ -0,0 +1,73 @@ + + * @copyright Microsoft Corporation + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @link https://github.com/windowsazure/azure-sdk-for-php + */ + +namespace WindowsAzure\MediaServices\Templates; + +/** + * Represents FairPlayConfiguration type used in media services + * + * @category Microsoft + * @package WindowsAzure\MediaServices\Templatesv + * @author Azure PHP SDK + * @copyright Microsoft Corporation + * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 + * @version Release: 0.4.2_2016-04 + * @link https://github.com/windowsazure/azure-sdk-for-php + */ +class FairPlayConfiguration +{ + /** + * @var string + */ + public $ASkId; + + /** + * @var string + */ + public $FairPlayPfxPasswordId; + + /** + * @var string + */ + public $FairPlayPfx; + + /** + * @var string + */ + public $ContentEncryptionIV; + + public static function createSerializedFairPlayOptionConfiguration($cert, $pkey, $pfxPassword, $pfxPasswordKeyId, $askId, $contentIv) { + + openssl_pkcs12_export($cert, $certBytes, $pkey, $pfxPassword); + $certString = base64_encode($certBytes); + $template = new FairPlayConfiguration(); + $template->ASkId = $askId; + $template->ContentEncryptionIV = $contentIv; + $template->FairPlayPfx = $certString; + $template->FairPlayPfxPasswordId = $pfxPasswordKeyId; + + return json_encode($template); + } + +} diff --git a/src/MediaServices/Templates/TokenRestrictionTemplateSerializer.php b/src/MediaServices/Templates/TokenRestrictionTemplateSerializer.php index 3c6b2ba0b..7899f538e 100644 --- a/src/MediaServices/Templates/TokenRestrictionTemplateSerializer.php +++ b/src/MediaServices/Templates/TokenRestrictionTemplateSerializer.php @@ -129,7 +129,7 @@ public static function serialize($tokenRestriction) if ($tokenRestriction->getAlternateVerificationKeys()) { self::serializeAlternateVerificationKeys($writer, $tokenRestriction->getAlternateVerificationKeys()); - } + } $writer->writeElement('Audience', $tokenRestriction->getAudience()); $writer->writeElement('Issuer', $tokenRestriction->getIssuer());