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