Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft Azure AES Encryption #358

Closed
boredom2 opened this issue Apr 28, 2016 · 22 comments
Closed

Microsoft Azure AES Encryption #358

boredom2 opened this issue Apr 28, 2016 · 22 comments
Labels
status: archived Archived and locked; will not be updated status: infeasible The requested feature is not currently feasible type: question A question from the community

Comments

@boredom2
Copy link

Hi there,
I am trying to integrate the 2.0 Release into our VideoPlayers - and so far its really working outstandingly great. Thanks for that.
Our usual problem is always - we have to make the MS Azure AES Encryption (Common Encryption) work with DASH - and thats always problematic :)
Currently, I do not really see, where I can get the necessary License Server URLs from the Manifest - and the "KID" information, that is needed as GET Parameter for that License Server.
Additionally, after that, there has to be a POST Request to that Server with that KID ID, containing a Token, that our Player will give Shaka as "Authorization" Header.
Did anyone from you Team already had this kind of implementation?
I have tons of MPD Files available, if you never saw this approach?

Thanks so much,
Christoph

@tdrews
Copy link
Contributor

tdrews commented Apr 28, 2016

Hi, you can configure Shaka v2 to use license server URLs specified in the manifest, and to use arbitrary query parameters in outbound license requests, as well as parse any inbound license responses.

For example, see https://github.com/google/shaka-player/blob/77a987e8e0183c89864474af43c79a6f44543519/demo/assets.js#L208
which parses

<ContentProtection schemeIdUri="http://youtube.com/drm/2012/10/10">
  <yt:SystemURL type="clearkey">url1</yt:SystemURL>
  <yt:SystemURL type="playready">url2</yt:SystemURL>
  <yt:SystemURL type="widevine">url3</yt:SystemURL>
</ContentProtection>

to set multiple license server URLs. This hook is set via

player.configure({
  manifest: { dash: { customScheme: shakaAssets.YouTubeCallback } }
});

You can use the same mechanism to set the PlayReady license server URL in your application. Also, see the DRM tutorial.

You can then use a network request filter to set query parameters, for example,

player.getNetworkingEngine().registerRequestFilter(function(type, request) {
    if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
      request.headers['auth_token'] = 'my_token';
    }
  });

and a network response filter to pull out any data you need in your application, for example,

player.getNetworkingEngine().registerResponseFilter(function(type, request) {
    if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
      app.appData = response.headers['app_data'];
    }
  });

Also, see the license server authentication tutorial.

Hope that helps. Let us know how it goes.

@tdrews tdrews added the type: question A question from the community label Apr 28, 2016
@boredom2
Copy link
Author

boredom2 commented May 1, 2016

Hi there. Thanks a lot for that detailed explanations.
Especially that manifest.dash approach was very helpfull.
Sadly, it wont be that easy :)
I am able to get now the License Server URL from the manifest (Thanks!).
And I already configured the registerRequestFilter - but there is never a ReqestType.LICENSE.
I am not sure, why.
The keySystem Information is:

<sea:SegmentEncryption schemeIdUri="urn:mpeg:dash:sea:aes128-cbc:2013">
<sea:KeySystem keySystemUri="urn:mpeg:dash:sea:keysys:http:2013">

SHAKA for sure does not know that KeySystem, so I declared in the config.manifest.dash.customScheme Callback:

configs=[];
configs.push({
    keySystem:'org.w3.clearkey',
    licenseServerUri:"manifest derived License Server URL",
...
});
return(configs);

Is it possible that there is no LICENSE Request, because SHAKA does not recognize the Manifest as ClearKey-secured? Or do you have any idea how to force SHAKA to perform this Request? :)

Thanks!
Christoph

@joeyparrish
Copy link
Member

If there is no license request, then Shaka has not received an encrypted event from the media stack. You may want to debug at shaka.media.DrmEngine.prototype.onEncrypted_.

Without an encrypted event containing initialization data, we can't ask the CDM to generate a license request. If there is init data in a cenc:pssh element in the manifest, we will use that data instead and ignore encrypted events from the browser.

@joeyparrish
Copy link
Member

Oh, and I should also add that browsers send 'encrypted' events when they encounter PSSH boxes in the media. If there is no PSSH box in the media, then init data will have to be provided via the manifest (cenc:pssh element) or your custom callback (initData field, an array of init data override objects).

@joeyparrish joeyparrish self-assigned this May 2, 2016
@boredom2
Copy link
Author

boredom2 commented May 2, 2016

Thanks fot that hint, seems like im learning a lot with this case :)
I can confirm, that there is indeed no "cenc:pssh" element in the MPD - so I will have to create an initData Override. I do see the documentation of that Attribute on your Api Docs and also your linked code - but everything is referencing a non-existing page (https://w3c.github.io/encrypted-media/initdata-format-registry.html#registry). So maybe you have any kind of example or template I can use for that?

@joeyparrish
Copy link
Member

Whoops, it looks like the spec has moved some things around. We'll get those links updated in our docs. In the mean time, here's the current link: https://w3c.github.io/encrypted-media/format-registry/initdata/#registry

For ClearKey content, you should use the keyids format, which is a JSON string. All you need are the key IDs. For example:

{
  "kids": [
    "LwVHf8JLtPrv2GUXFW2v_A",
    "0DdtU9od-Bh5L3xbv0Xf_A"
  ]
}

So something like this should do the trick:

var keyIds = [ /* collect your key IDs here */ ];

var initDataString = JSON.stringify({kids: keyIds});
var initData = new Uint8Array(initDataString.length);
for (var i = 0; i < initDataString.length; ++i) {
  initData[i] = initDataString.charCodeAt(i);
}

var initDataOverride = {
  initDataType: 'keyids',
  initData: initData
};
shaka-bot pushed a commit that referenced this issue May 2, 2016
The EME init data registry has recently moved.

Reported as part of #358

Change-Id: I54dec283e7f69bc31e31734728dc17255025b93f
@boredom2
Copy link
Author

boredom2 commented May 2, 2016

I am not convinced, that AES Encryption is really fun, especially, if MS is not really declaring, how to deal with it :)
Ok, the situation is like the following. Within the MPD (e.g. this one: "http://nexxplayplus1.streaming.mediaservices.windows.net/d9ccfb03-5bf8-42cc-bd98-e235b147fd80/73656_src.ism/Manifest(format=mpd-time-csf)") - we find these Protection Informations;

<sea:SegmentEncryption schemeIdUri="urn:mpeg:dash:sea:aes128-cbc:2013">
<sea:KeySystem keySystemUri="urn:mpeg:dash:sea:keysys:http:2013">
<sea:CryptoPeriod keyUriTemplate="https://wamsamsclus001kd-hs.cloudapp.net/?KID=ef07d917-5c6e-45d2-8ce2-28abac2a4e35" IV="0x29EB6D7A6EC2B30AB04DC63C2E4F010E">

Microsoft considers the "KID" Attribute of the KeyUriTemplate as Key IDs - so I took them and constructed some initData - but sadly, no success. Shaka is still not willing to make any License Call.
Forthermore - I am still not sure, if this could be really considered as "clearkey" Encrytpion at all.
The target ist, that the License Call will go to this keyUriTemplate where I will add a specific header token (which is given by our backend) . The License Call will, once accepted, then return another Key/Token, which should be used in order to decrypt the stream.
Microsoft states though, that the "IV" value should also be used in order to decrpyt the stream, but is not telling anywhere, how.

dash:{
                                customScheme:function(node){
                                    var configs=[];
                                    var keyIds=[];
                                    for(var i=0;i<node.childNodes.length;++i){
                                        var child=node.childNodes[i];
                                        if(child.nodeName=='sea:CryptoPeriod'){
                                            var u=child.getAttribute('keyUriTemplate'); //this will be the License Server ID
                                            keyIds.push(u.split('=')[1]); // assuming, that the KID value is the Key ID we will need. What is with "IV"?
                                        }
                                    }
                                    var initDataString=JSON.stringify({kids:keyIds});
                                    var initData=new Uint8Array(initDataString.length);
                                    for(var i=0;i<initDataString.length;++i){
                                        initData[i]=initDataString.charCodeAt(i);;
                                    }
                                    configs.push({
                                        keySystem:'org.w3.clearkey',
                                        licenseServerUri:u,
                                        distinctiveIdentifierRequired:false,
                                        persistentStateRequired: false,
                                        audioRobustness: '',
                                        videoRobustness: '',
                                        serverCertificate: null,
                                        initData: {
                                            initDataType:'keyids',
                                            initData:initData
                                        }
                                    });
                                    return configs;
                                }
                            }

Although we are in close contact with MS directly, its hard even for them to give us a correct answer how to dig this out correctly - which is quite sad actually :)
Who can we hire to make this work? :P
I know, this is a very specific question, but maybe someone from the experts here have an idea, how all this should be combined?

Thanks a lot!

@joeyparrish
Copy link
Member

The initData field in the config object is an array. That may be the problem.

initData: [{
  initDataType: 'keyids',
  initData: initData
}]
@boredom2
Copy link
Author

boredom2 commented May 2, 2016

Sharp eyes :) I changed that to Array.
Now I get Error 6006 - FAILED_TO_GENERATE_LICENSE_REQUEST, which is consistent, due to the fact, that I still dont see any License Call...
I additionally see the Error details "couldnt parse init data". I double checked that. An Array should be correct, the initDataType is correct and according to your documentation, initData must be an UInt8Array. This Array is present with 49 items currently, when taking the "KID" Attribute from above. I also tried with adding the "IV" Attribute as KeyId also or only using the "IV" Attribute.
Always the same error - "couldnt parse init data".

@joeyparrish
Copy link
Member

What browser, browser version, and OS are you using? It might help to track down why the init data can't be parsed.

@boredom2
Copy link
Author

boredom2 commented May 3, 2016

Wow, you are indeed right (all browsers latest version on Windows 10).
Normally I use Firefox (V47) for Development - and there, init data is indeed not parseable (same in MS Edge).
In Chrome (V50) - I do see a License Request! But there is also an error coming up (Reference Error: request is not defined at Array. at X.request).
Thats really interesing, seems like there are differences at that level in different browsers.

@joeyparrish
Copy link
Member

Last I checked, only Chrome, Firefox, and Opera had ClearKey. IE, Edge, and Safari did not, but it's possible Edge has introduced ClearKey support since I last looked.

Can you use an uncompiled version of Shaka and provide the complete log from Chrome?

@boredom2
Copy link
Author

boredom2 commented May 3, 2016

Hello again, I really tried to get this uncompiled version running, but I permanently get "shaka.Player is not a constructor" after getting from npm and following your code in /demo. Is there anything else, that is necessary to run the player uncompiled except

    <script src="third_party/closure/goog/base.js"></script>
    <script src="dist/deps.js"></script>
    <script src="shaka-player.uncompiled.js"></script>

As always - thanks a lot for your time!
Christoph

@joeyparrish
Copy link
Member

What version did you install from npm? "dist/deps.js" is the path for v2.0.0-beta, but not for the v1.6.5 release.

@boredom2
Copy link
Author

boredom2 commented May 3, 2016

Thats correct - but we are talking about the V2 Beta Version?

@joeyparrish
Copy link
Member

Yes, v2.0.0-beta is what we are discussing, so if you install from npm, make sure that is the version you have installed.

@boredom2
Copy link
Author

boredom2 commented May 3, 2016

Hahahha, yes, you are right. I didnt know, that the Syntax of 1.* and 2.* is that close. The NPM was indeed 1.6.5 - my fault, sorry for that.

The "not-parseable" initData Error on Firefox now has a Stack:
"shaka.util.Error@.../npm/node_modules/shaka-player/lib/util/error.js:77:13shaka.media.DrmEngine.prototype.createTemporarySession_/<@.../npm/node_modules/shaka-player/lib/media/drm_engine.js:653:19"

In Chrome though, its more complex:

"Error: Shaka Error MEDIA.VIDEO_ERROR (4,) at new shaka.util.Error (.../npm/node_modules/shaka-player/lib/util/error.js:77:13) at shaka.Player.onVideoError_ (.../npm/node_modules/shaka-player/lib/player.js:1482:17)"

Which then leads to "Assertion failed! Wrong error type!" on player.js:383

And taking all of this into consideration - I am still not sure (are you?) that "clearkeys" is really the right way to handle AES Encryption - and even if, if I am using the right keyids.

@joeyparrish
Copy link
Member

The Firefox stack trace shows that the EME implementation rejected the init data.

The Chrome stack trace shows that the video element threw MEDIA_ERR_SRC_NOT_SUPPORTED (outside of Shaka). Not sure what to make of this.

I'm not actually familiar with Azure's AES encryption. I've been working under the assumption that ClearKey was the right way to implement it on the client, and that the necessary key ID information was available in the manifest.

If you can provide me with URL for one of your manifests, I can try to help you determine the right way to proceed. If you can't provide the manifest publicly, you can send me a private email. My address is in the CONTRIBUTORS files.

@boredom2
Copy link
Author

boredom2 commented May 4, 2016

Hi, I do really appreciate all your help for sure :)
The problem is - nobody really is familiar with this "AES Encryption" and although we work closely with Microsoft, they also cannot really give me technical insights in how to do that correctly - its really a shame :)
Due to the fact, that they call this "Common Envelope Encryption", I do suspect, that "ClearKey" is the right way to do that - but thats just my guess.

I added an MPD File above, you can use it, its just for testing purposes:
http://nexxplayplus1.streaming.mediaservices.windows.net/d9ccfb03-5bf8-42cc-bd98-e235b147fd80/73656_src.ism/Manifest(format=mpd-time-csf)

Thanks a lot!

@joeyparrish joeyparrish removed their assignment May 6, 2016
@joeyparrish
Copy link
Member

This looks like HLS-style encryption, based on "AES128-CBC", a "key URI", and an explicit IV:

<sea:SegmentEncryption schemeIdUri="urn:mpeg:dash:sea:aes128-cbc:2013"/>
<sea:KeySystem keySystemUri="urn:mpeg:dash:sea:keysys:http:2013"/>
<sea:CryptoPeriod
  keyUriTemplate="https://wamsamsclus001kd-hs.cloudapp.net/?KID=ef07d917-5c6e-45d2-8ce2-28abac2a4e35"
  IV="0x29EB6D7A6EC2B30AB04DC63C2E4F010E"/>

I don't believe you're going to be able to do this with ClearKey, which as far I know currently requires AES128-CTR. Please seek further guidance from Microsoft.

@joeyparrish joeyparrish added the status: infeasible The requested feature is not currently feasible label May 6, 2016
@mingfeiy
Copy link

mingfeiy commented May 12, 2016

Hi Joeyparrish,

Does Shakar Player supports AES 128-CTR not CBC? This is the spec we Microsoft implemented for AES 128 clear key (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19). Clear Key we believe refers to how the key is delivered to the player and the encryption is technically separate. We chose to implement AES 128 CBC first since Apple devices supported.

Cheers,
Mingfei Yan

@joeyparrish
Copy link
Member

Shaka Player doesn't directly do any decryption. That's a matter for EME and for CDM implementations, which to my knowledge, use a version of Common Encryption (CENC) which only used AES-128-CTR. I have heard that a newer CENC spec is adding CBC, so it is possible that browsers may support such content in future.

To the best of my knowledge, the Clear Key CDM (which has been implemented in Chrome and Firefox and is the component that does the decryption) uses AES-128-CTR only at this time.

@shaka-project shaka-project locked and limited conversation to collaborators Mar 22, 2018
@shaka-bot shaka-bot added the status: archived Archived and locked; will not be updated label Apr 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
status: archived Archived and locked; will not be updated status: infeasible The requested feature is not currently feasible type: question A question from the community
5 participants