1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "NdkMediaDrm"
19
20#include "NdkMediaDrm.h"
21
22#include <utils/Log.h>
23#include <utils/StrongPointer.h>
24#include <gui/Surface.h>
25
26#include <media/IDrm.h>
27#include <media/IDrmClient.h>
28#include <media/stagefright/MediaErrors.h>
29#include <binder/IServiceManager.h>
30#include <media/IMediaPlayerService.h>
31#include <ndk/NdkMediaCrypto.h>
32
33
34using namespace android;
35
36typedef Vector<uint8_t> idvec_t;
37
38struct DrmListener: virtual public BnDrmClient
39{
40private:
41    AMediaDrm *mObj;
42    AMediaDrmEventListener mListener;
43
44public:
45    DrmListener(AMediaDrm *obj, AMediaDrmEventListener listener) : mObj(obj), mListener(listener) {}
46    void notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj);
47};
48
49struct AMediaDrm {
50    sp<IDrm> mDrm;
51    sp<IDrmClient> mDrmClient;
52    List<idvec_t> mIds;
53    KeyedVector<String8, String8> mQueryResults;
54    Vector<uint8_t> mKeyRequest;
55    Vector<uint8_t> mProvisionRequest;
56    String8 mProvisionUrl;
57    String8 mPropertyString;
58    Vector<uint8_t> mPropertyByteArray;
59    List<Vector<uint8_t> > mSecureStops;
60    sp<DrmListener> mListener;
61};
62
63void DrmListener::notify(DrmPlugin::EventType eventType, int extra, const Parcel *obj) {
64    if (!mListener) {
65        return;
66    }
67
68    AMediaDrmSessionId sessionId = {NULL, 0};
69    int32_t sessionIdSize = obj->readInt32();
70    if (sessionIdSize) {
71        uint8_t *sessionIdData = new uint8_t[sessionIdSize];
72        sessionId.ptr = sessionIdData;
73        sessionId.length = sessionIdSize;
74        obj->read(sessionIdData, sessionId.length);
75    }
76
77    int32_t dataSize = obj->readInt32();
78    uint8_t *data = NULL;
79    if (dataSize) {
80        data = new uint8_t[dataSize];
81        obj->read(data, dataSize);
82    }
83
84    // translate DrmPlugin event types into their NDK equivalents
85    AMediaDrmEventType ndkEventType;
86    switch(eventType) {
87        case DrmPlugin::kDrmPluginEventProvisionRequired:
88            ndkEventType = EVENT_PROVISION_REQUIRED;
89            break;
90        case DrmPlugin::kDrmPluginEventKeyNeeded:
91            ndkEventType = EVENT_KEY_REQUIRED;
92            break;
93        case DrmPlugin::kDrmPluginEventKeyExpired:
94            ndkEventType = EVENT_KEY_EXPIRED;
95            break;
96        case DrmPlugin::kDrmPluginEventVendorDefined:
97            ndkEventType = EVENT_VENDOR_DEFINED;
98            break;
99        default:
100            ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType);
101            return;
102    }
103
104    (*mListener)(mObj, &sessionId, ndkEventType, extra, data, dataSize);
105
106    delete [] sessionId.ptr;
107    delete [] data;
108}
109
110
111extern "C" {
112
113static media_status_t translateStatus(status_t status) {
114    media_status_t result = AMEDIA_ERROR_UNKNOWN;
115    switch (status) {
116        case OK:
117            result = AMEDIA_OK;
118            break;
119        case android::ERROR_DRM_NOT_PROVISIONED:
120            result = AMEDIA_DRM_NOT_PROVISIONED;
121            break;
122        case android::ERROR_DRM_RESOURCE_BUSY:
123            result = AMEDIA_DRM_RESOURCE_BUSY;
124            break;
125        case android::ERROR_DRM_DEVICE_REVOKED:
126            result = AMEDIA_DRM_DEVICE_REVOKED;
127            break;
128        case android::ERROR_DRM_CANNOT_HANDLE:
129            result = AMEDIA_ERROR_INVALID_PARAMETER;
130            break;
131        case android::ERROR_DRM_TAMPER_DETECTED:
132            result = AMEDIA_DRM_TAMPER_DETECTED;
133            break;
134        case android::ERROR_DRM_SESSION_NOT_OPENED:
135            result = AMEDIA_DRM_SESSION_NOT_OPENED;
136            break;
137        case android::ERROR_DRM_NO_LICENSE:
138            result = AMEDIA_DRM_NEED_KEY;
139            break;
140        case android::ERROR_DRM_LICENSE_EXPIRED:
141            result = AMEDIA_DRM_LICENSE_EXPIRED;
142            break;
143        default:
144            break;
145    }
146    return result;
147}
148
149static sp<IDrm> CreateDrm() {
150    sp<IServiceManager> sm = defaultServiceManager();
151
152    sp<IBinder> binder =
153        sm->getService(String16("media.player"));
154
155    sp<IMediaPlayerService> service =
156        interface_cast<IMediaPlayerService>(binder);
157
158    if (service == NULL) {
159        return NULL;
160    }
161
162    sp<IDrm> drm = service->makeDrm();
163
164    if (drm == NULL || (drm->initCheck() != OK && drm->initCheck() != NO_INIT)) {
165        return NULL;
166    }
167
168    return drm;
169}
170
171
172static sp<IDrm> CreateDrmFromUUID(const AMediaUUID uuid) {
173    sp<IDrm> drm = CreateDrm();
174
175    if (drm == NULL) {
176        return NULL;
177    }
178
179    status_t err = drm->createPlugin(uuid);
180
181    if (err != OK) {
182        return NULL;
183    }
184
185    return drm;
186}
187
188EXPORT
189bool AMediaDrm_isCryptoSchemeSupported(const AMediaUUID uuid, const char *mimeType) {
190    sp<IDrm> drm = CreateDrm();
191
192    if (drm == NULL) {
193        return false;
194    }
195
196    String8 mimeStr = mimeType ? String8(mimeType) : String8("");
197    return drm->isCryptoSchemeSupported(uuid, mimeStr);
198}
199
200EXPORT
201AMediaDrm* AMediaDrm_createByUUID(const AMediaUUID uuid) {
202    AMediaDrm *mObj = new AMediaDrm();
203    mObj->mDrm = CreateDrmFromUUID(uuid);
204    return mObj;
205}
206
207EXPORT
208void AMediaDrm_release(AMediaDrm *mObj) {
209    if (mObj->mDrm != NULL) {
210        mObj->mDrm->setListener(NULL);
211        mObj->mDrm->destroyPlugin();
212        mObj->mDrm.clear();
213    }
214    delete mObj;
215}
216
217EXPORT
218media_status_t AMediaDrm_setOnEventListener(AMediaDrm *mObj, AMediaDrmEventListener listener) {
219    if (!mObj || mObj->mDrm == NULL) {
220        return AMEDIA_ERROR_INVALID_OBJECT;
221    }
222    mObj->mListener = new DrmListener(mObj, listener);
223    mObj->mDrm->setListener(mObj->mListener);
224    return AMEDIA_OK;
225}
226
227
228static bool findId(AMediaDrm *mObj, const AMediaDrmByteArray &id, List<idvec_t>::iterator &iter) {
229    iter = mObj->mIds.begin();
230    while (iter != mObj->mIds.end()) {
231        if (iter->array() == id.ptr && iter->size() == id.length) {
232            return true;
233        }
234    }
235    return false;
236}
237
238EXPORT
239media_status_t AMediaDrm_openSession(AMediaDrm *mObj, AMediaDrmSessionId *sessionId) {
240    if (!mObj || mObj->mDrm == NULL) {
241        return AMEDIA_ERROR_INVALID_OBJECT;
242    }
243    if (!sessionId) {
244        return AMEDIA_ERROR_INVALID_PARAMETER;
245    }
246    Vector<uint8_t> session;
247    status_t status = mObj->mDrm->openSession(session);
248    if (status == OK) {
249        mObj->mIds.push_front(session);
250        List<idvec_t>::iterator iter = mObj->mIds.begin();
251        sessionId->ptr = iter->array();
252        sessionId->length = iter->size();
253    }
254    return AMEDIA_OK;
255}
256
257EXPORT
258media_status_t AMediaDrm_closeSession(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId) {
259    if (!mObj || mObj->mDrm == NULL) {
260        return AMEDIA_ERROR_INVALID_OBJECT;
261    }
262    if (!sessionId) {
263        return AMEDIA_ERROR_INVALID_PARAMETER;
264    }
265
266    List<idvec_t>::iterator iter;
267    if (!findId(mObj, *sessionId, iter)) {
268        return AMEDIA_DRM_SESSION_NOT_OPENED;
269    }
270    mObj->mDrm->closeSession(*iter);
271    mObj->mIds.erase(iter);
272    return AMEDIA_OK;
273}
274
275EXPORT
276media_status_t AMediaDrm_getKeyRequest(AMediaDrm *mObj, const AMediaDrmScope *scope,
277        const uint8_t *init, size_t initSize, const char *mimeType, AMediaDrmKeyType keyType,
278        const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
279        const uint8_t **keyRequest, size_t *keyRequestSize) {
280
281    if (!mObj || mObj->mDrm == NULL) {
282        return AMEDIA_ERROR_INVALID_OBJECT;
283    }
284    if (!mimeType || !scope || !keyRequest || !keyRequestSize) {
285        return AMEDIA_ERROR_INVALID_PARAMETER;
286    }
287
288    List<idvec_t>::iterator iter;
289    if (!findId(mObj, *scope, iter)) {
290        return AMEDIA_DRM_SESSION_NOT_OPENED;
291    }
292
293    Vector<uint8_t> mdInit;
294    mdInit.appendArray(init, initSize);
295    DrmPlugin::KeyType mdKeyType;
296    switch (keyType) {
297        case KEY_TYPE_STREAMING:
298            mdKeyType = DrmPlugin::kKeyType_Streaming;
299            break;
300        case KEY_TYPE_OFFLINE:
301            mdKeyType = DrmPlugin::kKeyType_Offline;
302            break;
303        case KEY_TYPE_RELEASE:
304            mdKeyType = DrmPlugin::kKeyType_Release;
305            break;
306        default:
307            return AMEDIA_ERROR_INVALID_PARAMETER;
308    }
309    KeyedVector<String8, String8> mdOptionalParameters;
310    for (size_t i = 0; i < numOptionalParameters; i++) {
311        mdOptionalParameters.add(String8(optionalParameters[i].mKey),
312                String8(optionalParameters[i].mValue));
313    }
314    String8 defaultUrl;
315    DrmPlugin::KeyRequestType keyRequestType;
316    status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType),
317            mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl,
318            &keyRequestType);
319    if (status != OK) {
320        return translateStatus(status);
321    } else {
322        *keyRequest = mObj->mKeyRequest.array();
323        *keyRequestSize = mObj->mKeyRequest.size();
324    }
325    return AMEDIA_OK;
326}
327
328EXPORT
329media_status_t AMediaDrm_provideKeyResponse(AMediaDrm *mObj, const AMediaDrmScope *scope,
330        const uint8_t *response, size_t responseSize, AMediaDrmKeySetId *keySetId) {
331
332    if (!mObj || mObj->mDrm == NULL) {
333        return AMEDIA_ERROR_INVALID_OBJECT;
334    }
335    if (!scope || !response || !responseSize || !keySetId) {
336        return AMEDIA_ERROR_INVALID_PARAMETER;
337    }
338
339    List<idvec_t>::iterator iter;
340    if (!findId(mObj, *scope, iter)) {
341        return AMEDIA_DRM_SESSION_NOT_OPENED;
342    }
343    Vector<uint8_t> mdResponse;
344    mdResponse.appendArray(response, responseSize);
345
346    Vector<uint8_t> mdKeySetId;
347    status_t status = mObj->mDrm->provideKeyResponse(*iter, mdResponse, mdKeySetId);
348    if (status == OK) {
349        mObj->mIds.push_front(mdKeySetId);
350        List<idvec_t>::iterator iter = mObj->mIds.begin();
351        keySetId->ptr = iter->array();
352        keySetId->length = iter->size();
353    } else {
354        keySetId->ptr = NULL;
355        keySetId->length = 0;
356    }
357    return AMEDIA_OK;
358}
359
360EXPORT
361media_status_t AMediaDrm_restoreKeys(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
362        const AMediaDrmKeySetId *keySetId) {
363
364    if (!mObj || mObj->mDrm == NULL) {
365        return AMEDIA_ERROR_INVALID_OBJECT;
366    }
367    if (!sessionId || !keySetId) {
368        return AMEDIA_ERROR_INVALID_PARAMETER;
369    }
370    List<idvec_t>::iterator iter;
371    if (!findId(mObj, *sessionId, iter)) {
372        return AMEDIA_DRM_SESSION_NOT_OPENED;
373    }
374    Vector<uint8_t> keySet;
375    keySet.appendArray(keySetId->ptr, keySetId->length);
376    return translateStatus(mObj->mDrm->restoreKeys(*iter, keySet));
377}
378
379EXPORT
380media_status_t AMediaDrm_removeKeys(AMediaDrm *mObj, const AMediaDrmSessionId *keySetId) {
381    if (!mObj || mObj->mDrm == NULL) {
382        return AMEDIA_ERROR_INVALID_OBJECT;
383    }
384    if (!keySetId) {
385        return AMEDIA_ERROR_INVALID_PARAMETER;
386    }
387    List<idvec_t>::iterator iter;
388    status_t status;
389    if (!findId(mObj, *keySetId, iter)) {
390        Vector<uint8_t> keySet;
391        keySet.appendArray(keySetId->ptr, keySetId->length);
392        status = mObj->mDrm->removeKeys(keySet);
393    } else {
394        status = mObj->mDrm->removeKeys(*iter);
395        mObj->mIds.erase(iter);
396    }
397    return translateStatus(status);
398}
399
400EXPORT
401media_status_t AMediaDrm_queryKeyStatus(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
402        AMediaDrmKeyValue *keyValuePairs, size_t *numPairs) {
403
404    if (!mObj || mObj->mDrm == NULL) {
405        return AMEDIA_ERROR_INVALID_OBJECT;
406    }
407    if (!sessionId || !numPairs) {
408        return AMEDIA_ERROR_INVALID_PARAMETER;
409    }
410    List<idvec_t>::iterator iter;
411    if (!findId(mObj, *sessionId, iter)) {
412        return AMEDIA_DRM_SESSION_NOT_OPENED;
413    }
414
415    status_t status = mObj->mDrm->queryKeyStatus(*iter, mObj->mQueryResults);
416    if (status != OK) {
417        *numPairs = 0;
418        return translateStatus(status);
419    }
420
421    if (mObj->mQueryResults.size() > *numPairs) {
422        *numPairs = mObj->mQueryResults.size();
423        return AMEDIA_DRM_SHORT_BUFFER;
424    }
425
426    for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
427        keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
428        keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
429    }
430    *numPairs = mObj->mQueryResults.size();
431    return AMEDIA_OK;
432}
433
434EXPORT
435media_status_t AMediaDrm_getProvisionRequest(AMediaDrm *mObj, const uint8_t **provisionRequest,
436        size_t *provisionRequestSize, const char **serverUrl) {
437    if (!mObj || mObj->mDrm == NULL) {
438        return AMEDIA_ERROR_INVALID_OBJECT;
439    }
440    if (!provisionRequest || !provisionRequestSize || !*provisionRequestSize || !serverUrl) {
441        return AMEDIA_ERROR_INVALID_PARAMETER;
442    }
443
444    status_t status = mObj->mDrm->getProvisionRequest(String8(""), String8(""),
445            mObj->mProvisionRequest, mObj->mProvisionUrl);
446    if (status != OK) {
447        return translateStatus(status);
448    } else {
449        *provisionRequest = mObj->mProvisionRequest.array();
450        *provisionRequestSize = mObj->mProvisionRequest.size();
451        *serverUrl = mObj->mProvisionUrl.string();
452    }
453    return AMEDIA_OK;
454}
455
456EXPORT
457media_status_t AMediaDrm_provideProvisionResponse(AMediaDrm *mObj,
458        const uint8_t *response, size_t responseSize) {
459    if (!mObj || mObj->mDrm == NULL) {
460        return AMEDIA_ERROR_INVALID_OBJECT;
461    }
462    if (!response || !responseSize) {
463        return AMEDIA_ERROR_INVALID_PARAMETER;
464    }
465
466    Vector<uint8_t> mdResponse;
467    mdResponse.appendArray(response, responseSize);
468
469    Vector<uint8_t> unused;
470    return translateStatus(mObj->mDrm->provideProvisionResponse(mdResponse, unused, unused));
471}
472
473EXPORT
474media_status_t AMediaDrm_getSecureStops(AMediaDrm *mObj,
475        AMediaDrmSecureStop *secureStops, size_t *numSecureStops) {
476
477    if (!mObj || mObj->mDrm == NULL) {
478        return AMEDIA_ERROR_INVALID_OBJECT;
479    }
480    if (!numSecureStops) {
481        return AMEDIA_ERROR_INVALID_PARAMETER;
482    }
483    status_t status = mObj->mDrm->getSecureStops(mObj->mSecureStops);
484    if (status != OK) {
485        *numSecureStops = 0;
486        return translateStatus(status);
487    }
488    if (*numSecureStops < mObj->mSecureStops.size()) {
489        return AMEDIA_DRM_SHORT_BUFFER;
490    }
491    List<Vector<uint8_t> >::iterator iter = mObj->mSecureStops.begin();
492    size_t i = 0;
493    while (iter != mObj->mSecureStops.end()) {
494        secureStops[i].ptr = iter->array();
495        secureStops[i].length = iter->size();
496        ++iter;
497        ++i;
498    }
499    *numSecureStops = mObj->mSecureStops.size();
500    return AMEDIA_OK;
501}
502
503EXPORT
504media_status_t AMediaDrm_releaseSecureStops(AMediaDrm *mObj,
505        const AMediaDrmSecureStop *ssRelease) {
506
507    if (!mObj || mObj->mDrm == NULL) {
508        return AMEDIA_ERROR_INVALID_OBJECT;
509    }
510    if (!ssRelease) {
511        return AMEDIA_ERROR_INVALID_PARAMETER;
512    }
513
514    Vector<uint8_t> release;
515    release.appendArray(ssRelease->ptr, ssRelease->length);
516    return translateStatus(mObj->mDrm->releaseSecureStops(release));
517}
518
519
520EXPORT
521media_status_t AMediaDrm_getPropertyString(AMediaDrm *mObj, const char *propertyName,
522        const char **propertyValue) {
523
524    if (!mObj || mObj->mDrm == NULL) {
525        return AMEDIA_ERROR_INVALID_OBJECT;
526    }
527    if (!propertyName || !propertyValue) {
528        return AMEDIA_ERROR_INVALID_PARAMETER;
529    }
530
531    status_t status = mObj->mDrm->getPropertyString(String8(propertyName),
532            mObj->mPropertyString);
533
534    if (status == OK) {
535        *propertyValue = mObj->mPropertyString.string();
536    } else {
537        *propertyValue = NULL;
538    }
539    return translateStatus(status);
540}
541
542EXPORT
543media_status_t AMediaDrm_getPropertyByteArray(AMediaDrm *mObj,
544        const char *propertyName, AMediaDrmByteArray *propertyValue) {
545    if (!mObj || mObj->mDrm == NULL) {
546        return AMEDIA_ERROR_INVALID_OBJECT;
547    }
548    if (!propertyName || !propertyValue) {
549        return AMEDIA_ERROR_INVALID_PARAMETER;
550    }
551
552    status_t status = mObj->mDrm->getPropertyByteArray(String8(propertyName),
553            mObj->mPropertyByteArray);
554
555    if (status == OK) {
556        propertyValue->ptr = mObj->mPropertyByteArray.array();
557        propertyValue->length = mObj->mPropertyByteArray.size();
558    } else {
559        propertyValue->ptr = NULL;
560        propertyValue->length = 0;
561    }
562    return translateStatus(status);
563}
564
565EXPORT
566media_status_t AMediaDrm_setPropertyString(AMediaDrm *mObj,
567        const char *propertyName, const char *value) {
568    if (!mObj || mObj->mDrm == NULL) {
569        return AMEDIA_ERROR_INVALID_OBJECT;
570    }
571
572    return translateStatus(mObj->mDrm->setPropertyString(String8(propertyName),
573                    String8(value)));
574}
575
576EXPORT
577media_status_t AMediaDrm_setPropertyByteArray(AMediaDrm *mObj,
578        const char *propertyName, const uint8_t *value, size_t valueSize) {
579
580    Vector<uint8_t> byteArray;
581    byteArray.appendArray(value, valueSize);
582
583    return translateStatus(mObj->mDrm->getPropertyByteArray(String8(propertyName),
584                    byteArray));
585}
586
587
588static media_status_t encrypt_decrypt_common(AMediaDrm *mObj,
589        const AMediaDrmSessionId &sessionId,
590        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
591        const uint8_t *input, uint8_t *output, size_t dataSize, bool encrypt) {
592
593    if (!mObj || mObj->mDrm == NULL) {
594        return AMEDIA_ERROR_INVALID_OBJECT;
595    }
596    List<idvec_t>::iterator iter;
597    if (!findId(mObj, sessionId, iter)) {
598        return AMEDIA_DRM_SESSION_NOT_OPENED;
599    }
600
601    status_t status = mObj->mDrm->setCipherAlgorithm(*iter, String8(cipherAlgorithm));
602    if (status != OK) {
603        return translateStatus(status);
604    }
605
606    Vector<uint8_t> keyIdVec;
607    const size_t kKeyIdSize = 16;
608    keyIdVec.appendArray(keyId, kKeyIdSize);
609
610    Vector<uint8_t> inputVec;
611    inputVec.appendArray(input, dataSize);
612
613    Vector<uint8_t> ivVec;
614    const size_t kIvSize = 16;
615    ivVec.appendArray(iv, kIvSize);
616
617    Vector<uint8_t> outputVec;
618    if (encrypt) {
619        status_t status = mObj->mDrm->encrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
620    } else {
621        status_t status = mObj->mDrm->decrypt(*iter, keyIdVec, inputVec, ivVec, outputVec);
622    }
623    if (status == OK) {
624        memcpy(output, outputVec.array(), outputVec.size());
625    }
626    return translateStatus(status);
627}
628
629EXPORT
630media_status_t AMediaDrm_encrypt(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
631        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
632        const uint8_t *input, uint8_t *output, size_t dataSize) {
633    if (!sessionId) {
634        return AMEDIA_ERROR_INVALID_PARAMETER;
635    }
636    return encrypt_decrypt_common(mObj, *sessionId, cipherAlgorithm, keyId, iv,
637            input, output, dataSize, true);
638}
639
640EXPORT
641media_status_t AMediaDrm_decrypt(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
642        const char *cipherAlgorithm, uint8_t *keyId, uint8_t *iv,
643        const uint8_t *input, uint8_t *output, size_t dataSize) {
644    if (!sessionId) {
645        return AMEDIA_ERROR_INVALID_PARAMETER;
646    }
647    return encrypt_decrypt_common(mObj, *sessionId, cipherAlgorithm, keyId, iv,
648            input, output, dataSize, false);
649}
650
651EXPORT
652media_status_t AMediaDrm_sign(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
653        const char *macAlgorithm, uint8_t *keyId, uint8_t *message, size_t messageSize,
654        uint8_t *signature, size_t *signatureSize) {
655
656    if (!mObj || mObj->mDrm == NULL) {
657        return AMEDIA_ERROR_INVALID_OBJECT;
658    }
659    if (!sessionId) {
660        return AMEDIA_ERROR_INVALID_PARAMETER;
661    }
662    List<idvec_t>::iterator iter;
663    if (!findId(mObj, *sessionId, iter)) {
664        return AMEDIA_DRM_SESSION_NOT_OPENED;
665    }
666
667    status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
668    if (status != OK) {
669        return translateStatus(status);
670    }
671
672    Vector<uint8_t> keyIdVec;
673    const size_t kKeyIdSize = 16;
674    keyIdVec.appendArray(keyId, kKeyIdSize);
675
676    Vector<uint8_t> messageVec;
677    messageVec.appendArray(message, messageSize);
678
679    Vector<uint8_t> signatureVec;
680    status = mObj->mDrm->sign(*iter, keyIdVec, messageVec, signatureVec);
681    if (signatureVec.size() > *signatureSize) {
682        return AMEDIA_DRM_SHORT_BUFFER;
683    }
684    if (status == OK) {
685        memcpy(signature, signatureVec.array(), signatureVec.size());
686    }
687    return translateStatus(status);
688}
689
690EXPORT
691media_status_t AMediaDrm_verify(AMediaDrm *mObj, const AMediaDrmSessionId *sessionId,
692        const char *macAlgorithm, uint8_t *keyId, const uint8_t *message, size_t messageSize,
693        const uint8_t *signature, size_t signatureSize) {
694
695    if (!mObj || mObj->mDrm == NULL) {
696        return AMEDIA_ERROR_INVALID_OBJECT;
697    }
698    if (!sessionId) {
699        return AMEDIA_ERROR_INVALID_PARAMETER;
700    }
701    List<idvec_t>::iterator iter;
702    if (!findId(mObj, *sessionId, iter)) {
703        return AMEDIA_DRM_SESSION_NOT_OPENED;
704    }
705
706    status_t status = mObj->mDrm->setMacAlgorithm(*iter, String8(macAlgorithm));
707    if (status != OK) {
708        return translateStatus(status);
709    }
710
711    Vector<uint8_t> keyIdVec;
712    const size_t kKeyIdSize = 16;
713    keyIdVec.appendArray(keyId, kKeyIdSize);
714
715    Vector<uint8_t> messageVec;
716    messageVec.appendArray(message, messageSize);
717
718    Vector<uint8_t> signatureVec;
719    signatureVec.appendArray(signature, signatureSize);
720
721    bool match;
722    status = mObj->mDrm->verify(*iter, keyIdVec, messageVec, signatureVec, match);
723    if (status == OK) {
724        return match ? AMEDIA_OK : AMEDIA_DRM_VERIFY_FAILED;
725    }
726    return translateStatus(status);
727}
728
729} // extern "C"
730