From 113d4d0755dc47348db40ad5d9f73f13355f86c4 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Tue, 5 Nov 2013 11:25:59 -0800 Subject: [PATCH] Add end-to-end generic encrypt/decrypt/sign/verify tests bug: 11534771 Merge of https://widevine-internal-review.googlesource.com/#/c/8291/ from the Widevine CDM repo. Change-Id: I76110013838f91c8009fe710586226c96de890be --- .../src/com/widevine/test/KeyRequester.java | 31 +-- .../com/widevine/test/MediaDrmAPITest.java | 206 +++++++++++++++++- 2 files changed, 218 insertions(+), 19 deletions(-) diff --git a/libwvdrmengine/test/java/src/com/widevine/test/KeyRequester.java b/libwvdrmengine/test/java/src/com/widevine/test/KeyRequester.java index cbab3833..8592bdd8 100644 --- a/libwvdrmengine/test/java/src/com/widevine/test/KeyRequester.java +++ b/libwvdrmengine/test/java/src/com/widevine/test/KeyRequester.java @@ -16,6 +16,7 @@ import java.util.Arrays; import android.os.AsyncTask; import android.util.Log; + public class KeyRequester { private final String TAG = "KeyRequester"; @@ -121,9 +122,12 @@ public class KeyRequester { try { // Add data ByteArrayEntity entity = new ByteArrayEntity(drmRequest); - entity.setChunked(true); + httppost.setEntity(entity); httppost.setHeader("User-Agent", "Widevine CDM v1.0"); + httppost.setHeader("Connection", "close"); + + Log.d(TAG, "request line=" + httppost.getRequestLine().toString()); // Execute HTTP Post Request HttpResponse response = httpclient.execute(httppost); @@ -161,20 +165,19 @@ public class KeyRequester { return null; } - if (!bodyString.startsWith("GLS/")) { - Log.e(TAG, "Invalid response from server, expected GLS/"); - return null; + if (bodyString.startsWith("GLS/")) { + if (!bodyString.startsWith("GLS/1.")) { + Log.e(TAG, "Invalid server version, expected 1.x"); + return null; + } + int drmMessageOffset = bodyString.indexOf("\r\n\r\n"); + if (drmMessageOffset == -1) { + Log.e(TAG, "Invalid server response, could not locate drm message"); + return null; + } + responseBody = Arrays.copyOfRange(responseBody, drmMessageOffset + 4, responseBody.length); } - if (!bodyString.startsWith("GLS/1.")) { - Log.e(TAG, "Invalid server version, expected 1.x"); - return null; - } - int drmMessageOffset = bodyString.indexOf("\r\n\r\n"); - if (drmMessageOffset == -1) { - Log.e(TAG, "Invalid server response, could not locate drm message"); - return null; - } - return Arrays.copyOfRange(responseBody, drmMessageOffset + 4, responseBody.length); + return responseBody; } private void sleep(int msec) { diff --git a/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java b/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java index 117d6b9c..31ca1551 100644 --- a/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java +++ b/libwvdrmengine/test/java/src/com/widevine/test/MediaDrmAPITest.java @@ -13,6 +13,7 @@ import android.view.SurfaceHolder; import android.view.Surface; import android.content.Context; import android.media.MediaDrm; +import android.media.MediaDrm.CryptoSession; import android.media.MediaDrmException; import android.media.NotProvisionedException; import android.media.MediaCrypto; @@ -72,13 +73,10 @@ class SurfacePanel extends SurfaceView implements SurfaceHolder.Callback public class MediaDrmAPITest extends Activity { private final String TAG = "MediaDrmAPITest"; - static final UUID kWidevineScheme = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL); - static final String kClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; static final String kKeyServerUrl = "https://jmt17.google.com/video-dev/license/GetCencLicense"; - static final String kPort = "80"; + static final String kOperatorSessionKeyServerUrl = "http://kir03wwwg185.widevine.net/drm"; - static final byte[] kPssh = hex2ba(// pssh data - "08011210e02562e04cd55351b14b3d748d36ed8e"); + static final UUID kWidevineScheme = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL); private boolean mTestFailed; @@ -99,6 +97,10 @@ public class MediaDrmAPITest extends Activity { testQueryKeyStatus(); testClearContentNoKeys(); testEncryptedContent(); + testGenericEncryptAndDecrypt(); + testGenericSign(); + testGenericVerify(); + testGenericMultipleKeys(); if (mTestFailed) { Log.e(TAG, "TEST FAILED!"); @@ -238,6 +240,9 @@ public class MediaDrmAPITest extends Activity { } private void getKeys(MediaDrm drm, byte[] sessionId) { + final byte[] kPssh = hex2ba("08011210e02562e04cd55351b14b3d748d36ed8e"); + final String kClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine"; + final String kPort = "80"; KeyRequester keyRequester = new KeyRequester(kPssh, kKeyServerUrl + ":" + kPort + kClientAuth); keyRequester.doTransact(drm, sessionId); } @@ -251,6 +256,197 @@ public class MediaDrmAPITest extends Activity { stopDrm(drm); } + private void testGenericEncryptAndDecrypt() { + final byte[] kOperatorSessionAESPssh = hex2ba("080112103be2b25db355fc64a0e69a50f4dbb298"); + + MediaDrm drm = startDrm(); + byte[] sessionId = openSession(drm); + + KeyRequester keyRequester = new KeyRequester(kOperatorSessionAESPssh, kOperatorSessionKeyServerUrl); + keyRequester.doTransact(drm, sessionId); + + CryptoSession cs = drm.getCryptoSession(sessionId, "AES/CBC/NoPadding", "HmacSHA256"); + + // operator_session_key_permissions=allow_encrypt | allow_decrypt + byte[] aes_key_id = hex2ba("3be2b25db355fc64a0e69a50f4dbb298"); + byte[] aes_key = hex2ba("5762d22a5e17d5402dc310a7c33ce539"); + + byte[] clr_data = hex2ba("4c02bcc3943aa828ecf7bbb16420572d00cabb21c3084c422217fee7fadd766d" + + "4bf726a232d029a81830e40e1e12ba34ba005ca6ce8033a0e3602a52b9b8d3d4" + + "b15dc458730f8affebbf35b1536c1a5d42370cf93c5b4094c0920bb1b2333f6a" + + "1897c5dd62eadfc1060786b0f69f228d5d7241cc644b85c35b9a7f4b893b5b85"); + byte[] enc_data = hex2ba("ee92d402f55f1d7eef4844803d9d43a603a4dd13f4a2ad5ea7653adcefa74b06" + + "ff459ab476a497567198c7cfa06d6fdd66b7924c2ca430baf534c5e00c800a06" + + "d0c108057ceab2ea33671d83e35eaaf1f1ff0f7e1618d05810a47b12cbc0806f" + + "d6ba8127a5e411f49b4e1790b1a6cb963e33d6cbc6569c44b4e1b28005fd1dde"); + + byte[] iv = hex2ba("3ec0f3d3970fbd541ac4e7e1d06a6131"); + + byte[] result = cs.decrypt(aes_key_id, enc_data, iv); + if (Arrays.equals(clr_data, result)) { + Log.d(TAG, "Decrypt test passed!"); + } else { + Log.d(TAG, "Decrypt test failed!"); + mTestFailed = true; + } + + result = cs.encrypt(aes_key_id, clr_data, iv); + if (Arrays.equals(enc_data, result)) { + Log.d(TAG, "Encrypt test passed!"); + } else { + Log.d(TAG, "Encrypt test failed!"); + mTestFailed = true; + } + drm.closeSession(sessionId); + stopDrm(drm); + } + + private void testGenericSign() { + final byte[] kOperatorSessionSignPssh = hex2ba("080112102685086ee9cb5835b063ab20786ffd78"); + + MediaDrm drm = startDrm(); + byte[] sessionId = openSession(drm); + KeyRequester keyRequester = new KeyRequester(kOperatorSessionSignPssh, kOperatorSessionKeyServerUrl); + keyRequester.doTransact(drm, sessionId); + + CryptoSession cs = drm.getCryptoSession(sessionId, "AES/CBC/NoPadding", "HmacSHA256"); + + // operator_session_key_permissions=allow_sign + byte[] signing_key_id = hex2ba("2685086ee9cb5835b063ab20786ffd78"); + byte[] signing_key = hex2ba("b3dddf87d1cfc0b04c9253231ff89b9e374ef2f424edc7b7f4b2c10e39768ee8"); + + byte[] data = hex2ba("4c02bcc3943aa828ecf7bbb16420572d00cabb21c3084c422217fee7fadd766d" + + "4bf726a232d029a81830e40e1e12ba34ba005ca6ce8033a0e3602a52b9b8d3d4" + + "b15dc458730f8affebbf35b1536c1a5d42370cf93c5b4094c0920bb1b2333f6a" + + "1897c5dd62eadfc1060786b0f69f228d5d7241cc644b85c35b9a7f4b893b5b85"); + + byte[] hmacsha256 = hex2ba("5fc29a4c15fcf9e1e26b63b3be169d7f53e61e1564b92876f70c9ffd17697437"); + + byte[] result = cs.sign(signing_key_id, data); + if (Arrays.equals(hmacsha256, result)) { + Log.d(TAG, "Signing test passed!"); + } else { + Log.d(TAG, "Signing test failed!"); + mTestFailed = true; + } + drm.closeSession(sessionId); + stopDrm(drm); + } + + private void testGenericVerify() { + MediaDrm drm = startDrm(); + byte[] sessionId = openSession(drm); + final byte[] kOperatorSessionVerifyPssh = hex2ba("0801121097c003f73b1a53aa51ba54a6ef631ca0"); + + KeyRequester keyRequester = new KeyRequester(kOperatorSessionVerifyPssh, kOperatorSessionKeyServerUrl); + keyRequester.doTransact(drm, sessionId); + + CryptoSession cs = drm.getCryptoSession(sessionId, "AES/CBC/NoPadding", "HmacSHA256"); + + // operator_session_key_permissions=allow_signature_verify + byte[] verify_key_id = hex2ba("97c003f73b1a53aa51ba54a6ef631ca0"); + byte[] verify_key = hex2ba("cfe2acb04ad5169153690c1932d5d2c6062a4607d274901e935d27b77ad48b2e"); + + byte[] data = hex2ba("4c02bcc3943aa828ecf7bbb16420572d00cabb21c3084c422217fee7fadd766d" + + "4bf726a232d029a81830e40e1e12ba34ba005ca6ce8033a0e3602a52b9b8d3d4" + + "b15dc458730f8affebbf35b1536c1a5d42370cf93c5b4094c0920bb1b2333f6a" + + "1897c5dd62eadfc1060786b0f69f228d5d7241cc644b85c35b9a7f4b893b5b85"); + + byte[] hmacsha256 = hex2ba("6bd61722e5cc3e698d536317309940328ab973be3a3b5705650aa09a48ebbf61"); + + if (cs.verify(verify_key_id, data, hmacsha256)) { + Log.d(TAG, "Verify test passed!"); + } else { + Log.d(TAG, "Verify test failed!"); + mTestFailed = true; + } + drm.closeSession(sessionId); + stopDrm(drm); + } + + private void testGenericMultipleKeys() { + final byte[] kOperatorSessionAESPssh = hex2ba("080112303be2b25db355fc64a0e69a50f4dbb2982685086ee9cb" + + "5835b063ab20786ffd7897c003f73b1a53aa51ba54a6ef631ca0"); + + MediaDrm drm = startDrm(); + byte[] sessionId = openSession(drm); + + KeyRequester keyRequester = new KeyRequester(kOperatorSessionAESPssh, kOperatorSessionKeyServerUrl); + keyRequester.doTransact(drm, sessionId); + + CryptoSession cs = drm.getCryptoSession(sessionId, "AES/CBC/NoPadding", "HmacSHA256"); + + // operator_session_key_permissions=allow_encrypt | allow_decrypt + byte[] aes_key_id = hex2ba("3be2b25db355fc64a0e69a50f4dbb298"); + byte[] aes_key = hex2ba("5762d22a5e17d5402dc310a7c33ce539"); + + byte[] clr_data = hex2ba("4c02bcc3943aa828ecf7bbb16420572d00cabb21c3084c422217fee7fadd766d" + + "4bf726a232d029a81830e40e1e12ba34ba005ca6ce8033a0e3602a52b9b8d3d4" + + "b15dc458730f8affebbf35b1536c1a5d42370cf93c5b4094c0920bb1b2333f6a" + + "1897c5dd62eadfc1060786b0f69f228d5d7241cc644b85c35b9a7f4b893b5b85"); + byte[] enc_data = hex2ba("ee92d402f55f1d7eef4844803d9d43a603a4dd13f4a2ad5ea7653adcefa74b06" + + "ff459ab476a497567198c7cfa06d6fdd66b7924c2ca430baf534c5e00c800a06" + + "d0c108057ceab2ea33671d83e35eaaf1f1ff0f7e1618d05810a47b12cbc0806f" + + "d6ba8127a5e411f49b4e1790b1a6cb963e33d6cbc6569c44b4e1b28005fd1dde"); + + byte[] iv = hex2ba("3ec0f3d3970fbd541ac4e7e1d06a6131"); + + byte[] result = cs.decrypt(aes_key_id, enc_data, iv); + if (Arrays.equals(clr_data, result)) { + Log.d(TAG, "Multiple key decrypt test passed!"); + } else { + Log.d(TAG, "Multiple key decrypt test failed!"); + mTestFailed = true; + } + + result = cs.encrypt(aes_key_id, clr_data, iv); + if (Arrays.equals(enc_data, result)) { + Log.d(TAG, "Multiple key encrypt test passed!"); + } else { + Log.d(TAG, "Multiple key encrypt test failed!"); + mTestFailed = true; + } + + byte[] signing_key_id = hex2ba("2685086ee9cb5835b063ab20786ffd78"); + byte[] signing_key = hex2ba("b3dddf87d1cfc0b04c9253231ff89b9e374ef2f424edc7b7f4b2c10e39768ee8"); + + byte[] signing_data = hex2ba("4c02bcc3943aa828ecf7bbb16420572d00cabb21c3084c422217fee7fadd766d" + + "4bf726a232d029a81830e40e1e12ba34ba005ca6ce8033a0e3602a52b9b8d3d4" + + "b15dc458730f8affebbf35b1536c1a5d42370cf93c5b4094c0920bb1b2333f6a" + + "1897c5dd62eadfc1060786b0f69f228d5d7241cc644b85c35b9a7f4b893b5b85"); + + byte[] hmacsha256 = hex2ba("5fc29a4c15fcf9e1e26b63b3be169d7f53e61e1564b92876f70c9ffd17697437"); + + result = cs.sign(signing_key_id, signing_data); + if (Arrays.equals(hmacsha256, result)) { + Log.d(TAG, "Multiple key signing test passed!"); + } else { + Log.d(TAG, "Multiple key signing test failed!"); + mTestFailed = true; + } + + // operator_session_key_permissions=allow_signature_verify + byte[] verify_key_id = hex2ba("97c003f73b1a53aa51ba54a6ef631ca0"); + byte[] verify_key = hex2ba("cfe2acb04ad5169153690c1932d5d2c6062a4607d274901e935d27b77ad48b2e"); + + byte[] verify_data = hex2ba("4c02bcc3943aa828ecf7bbb16420572d00cabb21c3084c422217fee7fadd766d" + + "4bf726a232d029a81830e40e1e12ba34ba005ca6ce8033a0e3602a52b9b8d3d4" + + "b15dc458730f8affebbf35b1536c1a5d42370cf93c5b4094c0920bb1b2333f6a" + + "1897c5dd62eadfc1060786b0f69f228d5d7241cc644b85c35b9a7f4b893b5b85"); + + hmacsha256 = hex2ba("6bd61722e5cc3e698d536317309940328ab973be3a3b5705650aa09a48ebbf61"); + + if (cs.verify(verify_key_id, verify_data, hmacsha256)) { + Log.d(TAG, "Multiple key verify test passed!"); + } else { + Log.d(TAG, "Multiple key verify test failed!"); + mTestFailed = true; + } + + drm.closeSession(sessionId); + stopDrm(drm); + } + private void testClearContentNoKeys() { MediaDrm drm = startDrm(); byte[] sessionId = openSession(drm);