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

Retrieving AES-128 Decryption Key Dynamically #6803

Open
rsmvdl opened this issue Oct 24, 2024 · 3 comments
Open

Retrieving AES-128 Decryption Key Dynamically #6803

rsmvdl opened this issue Oct 24, 2024 · 3 comments
Labels

Comments

@rsmvdl
Copy link

rsmvdl commented Oct 24, 2024

What do you want to do with Hls.js?

Hello,

I would like to inquire about the dynamic retrieval of an AES-128 decryption key. In the following scenario, is there a way to achieve this?

setXhrSetup() {
  this.videoJsConfigObj.html5!.hlsjsConfig!.xhrSetup = (xhr: any, url: string) => {
    if (url.includes('kms://')) {
      xhr.open('GET', url.replace('kms://', environment.kms_backend), true);
      xhr.setRequestHeader('Authorization', this.file_response.keymerchant_token);
    } else {
      xhr.open('GET', url, true);
      xhr.setRequestHeader('Authorization', this.file_response.access_token);
    }
    xhr.withCredentials = true;
  };
}

Currently, I overwrite the URL when it contains kms://, which references the key_id in my .m3u8 playlist. While this functionality is working well, I also need a method to pass the AES-128 key to HLS.js. Is it possible to request the decryption key from a WASM binary and then return it to HLS.js?
Proposed Idea:

The player makes an XHR request.
We intercept the XHR request as shown above.
We request the AES-128 decryption key from the WASM binary by passing the keymerchant_token and key_id.
The WASM binary returns the key as a Uint8Array.

This approach focuses solely on exchanging static AES-128 keys and does not involve EME/DRM/Widevine. My goal is to implement a protection mechanism that is a bit more complex than a simple static key exchange. Since I already have the WASM code for secure key exchange, I need assistance in passing the received Uint8Array back to HLS.js. Any Idea?

I'm aware that this does not bring the same protection as a real DRM, but still would make it quite hard to dump the decryption key from memory once it has been exchanged.

Thank you for your help!

What have you tried so far?

No response

@rsmvdl rsmvdl added Needs Triage If there is a suspected stream issue, apply this label to triage if it is something we should fix. Question labels Oct 24, 2024
@robwalch
Copy link
Collaborator

robwalch commented Oct 24, 2024

To return a custom result, you need to create a custom loader. Extending the XHR loader allows you to override the load method and evoke a successful callback with any payload.

https://github.com/video-dev/hls.js/blob/v1.5.0/docs/API.md#creating-a-custom-loader

@robwalch robwalch removed the Needs Triage If there is a suspected stream issue, apply this label to triage if it is something we should fix. label Oct 24, 2024
@rsmvdl
Copy link
Author

rsmvdl commented Oct 27, 2024

@robwalch First, thanks for your quick answer. Well, I tried to implement my own loader, but I went into issues regarding xhr requests, as they always get sent without the needed JWT token ... It appears that as soon as I use my own loader, xhrSetup does not get configured anymore, see my code above. I can also not use a loader in combination with the code I showed above, so how can I use my own loader and my own xhr request setup both at the same time? Do I have to implement everything myself inside my own loader ?

Thanks in advance

@robwalch
Copy link
Collaborator

You can set request headers and withCredentials in your custom loader instead of using xhrSetup. If you still want to use xhrSetup in a custom loader, implement it as it is in the xhr-loader (

loadInternal() {
const { config, context } = this;
if (!config || !context) {
return;
}
const xhr = (this.loader = new self.XMLHttpRequest());
const stats = this.stats;
stats.loading.first = 0;
stats.loaded = 0;
stats.aborted = false;
const xhrSetup = this.xhrSetup;
if (xhrSetup) {
Promise.resolve()
.then(() => {
if (this.loader !== xhr || this.stats.aborted) return;
return xhrSetup(xhr, context.url);
})
.catch((error: Error) => {
if (this.loader !== xhr || this.stats.aborted) return;
xhr.open('GET', context.url, true);
return xhrSetup(xhr, context.url);
})
.then(() => {
if (this.loader !== xhr || this.stats.aborted) return;
this.openAndSendXhr(xhr, context, config);
})
.catch((error: Error) => {
// IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS
this.callbacks!.onError(
{ code: xhr.status, text: error.message },
context,
xhr,
stats,
);
return;
});
} else {
this.openAndSendXhr(xhr, context, config);
}
), or make your loader extend XhrLoader, call super() in the constructor, and call load or loadInternal to start loading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants