| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define LOG_NDEBUG 0 |
| #define LOG_TAG "ICrypto" |
| #include <binder/Parcel.h> |
| #include <binder/IMemory.h> |
| #include <cutils/log.h> |
| #include <media/stagefright/MediaErrors.h> |
| #include <media/stagefright/foundation/ADebug.h> |
| #include <media/stagefright/foundation/AString.h> |
| #include <mediadrm/ICrypto.h> |
| #include <utils/Log.h> |
| |
| namespace android { |
| |
| enum { |
| INIT_CHECK = IBinder::FIRST_CALL_TRANSACTION, |
| IS_CRYPTO_SUPPORTED, |
| CREATE_PLUGIN, |
| DESTROY_PLUGIN, |
| REQUIRES_SECURE_COMPONENT, |
| DECRYPT, |
| NOTIFY_RESOLUTION, |
| SET_MEDIADRM_SESSION, |
| SET_HEAP, |
| UNSET_HEAP, |
| }; |
| |
| struct BpCrypto : public BpInterface<ICrypto> { |
| explicit BpCrypto(const sp<IBinder> &impl) |
| : BpInterface<ICrypto>(impl) { |
| } |
| |
| virtual status_t initCheck() const { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| remote()->transact(INIT_CHECK, data, &reply); |
| |
| return reply.readInt32(); |
| } |
| |
| virtual bool isCryptoSchemeSupported(const uint8_t uuid[16]) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.write(uuid, 16); |
| remote()->transact(IS_CRYPTO_SUPPORTED, data, &reply); |
| |
| return reply.readInt32() != 0; |
| } |
| |
| virtual status_t createPlugin( |
| const uint8_t uuid[16], const void *opaqueData, size_t opaqueSize) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.write(uuid, 16); |
| data.writeInt32(opaqueSize); |
| |
| if (opaqueSize > 0) { |
| data.write(opaqueData, opaqueSize); |
| } |
| |
| remote()->transact(CREATE_PLUGIN, data, &reply); |
| |
| return reply.readInt32(); |
| } |
| |
| virtual status_t destroyPlugin() { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| remote()->transact(DESTROY_PLUGIN, data, &reply); |
| |
| return reply.readInt32(); |
| } |
| |
| virtual bool requiresSecureDecoderComponent( |
| const char *mime) const { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.writeCString(mime); |
| remote()->transact(REQUIRES_SECURE_COMPONENT, data, &reply); |
| |
| return reply.readInt32() != 0; |
| } |
| |
| virtual ssize_t decrypt(const uint8_t key[16], const uint8_t iv[16], |
| CryptoPlugin::Mode mode, const CryptoPlugin::Pattern &pattern, |
| const SourceBuffer &source, size_t offset, |
| const CryptoPlugin::SubSample *subSamples, size_t numSubSamples, |
| const DestinationBuffer &destination, AString *errorDetailMsg) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.writeInt32(mode); |
| data.writeInt32(pattern.mEncryptBlocks); |
| data.writeInt32(pattern.mSkipBlocks); |
| |
| static const uint8_t kDummy[16] = { 0 }; |
| |
| if (key == NULL) { |
| key = kDummy; |
| } |
| |
| if (iv == NULL) { |
| iv = kDummy; |
| } |
| |
| data.write(key, 16); |
| data.write(iv, 16); |
| |
| size_t totalSize = 0; |
| for (size_t i = 0; i < numSubSamples; ++i) { |
| totalSize += subSamples[i].mNumBytesOfEncryptedData; |
| totalSize += subSamples[i].mNumBytesOfClearData; |
| } |
| |
| data.writeInt32(totalSize); |
| data.writeStrongBinder(IInterface::asBinder(source.mSharedMemory)); |
| data.writeInt32(source.mHeapSeqNum); |
| data.writeInt32(offset); |
| |
| data.writeInt32(numSubSamples); |
| data.write(subSamples, sizeof(CryptoPlugin::SubSample) * numSubSamples); |
| |
| data.writeInt32((int32_t)destination.mType); |
| if (destination.mType == kDestinationTypeNativeHandle) { |
| if (destination.mHandle == NULL) { |
| return BAD_VALUE; |
| } |
| data.writeNativeHandle(destination.mHandle); |
| } else { |
| if (destination.mSharedMemory == NULL) { |
| return BAD_VALUE; |
| } |
| data.writeStrongBinder(IInterface::asBinder(destination.mSharedMemory)); |
| } |
| |
| remote()->transact(DECRYPT, data, &reply); |
| |
| ssize_t result = reply.readInt32(); |
| |
| if (isCryptoError(result)) { |
| AString msg = reply.readCString(); |
| if (errorDetailMsg) { |
| *errorDetailMsg = msg; |
| } |
| } |
| |
| return result; |
| } |
| |
| virtual void notifyResolution( |
| uint32_t width, uint32_t height) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.writeInt32(width); |
| data.writeInt32(height); |
| remote()->transact(NOTIFY_RESOLUTION, data, &reply); |
| } |
| |
| virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| |
| writeVector(data, sessionId); |
| remote()->transact(SET_MEDIADRM_SESSION, data, &reply); |
| |
| return reply.readInt32(); |
| } |
| |
| virtual int32_t setHeap(const sp<IMemoryHeap> &heap) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.writeStrongBinder(IInterface::asBinder(heap)); |
| status_t err = remote()->transact(SET_HEAP, data, &reply); |
| if (err != NO_ERROR) { |
| return -1; |
| } |
| int32_t seqNum; |
| if (reply.readInt32(&seqNum) != NO_ERROR) { |
| return -1; |
| } |
| return seqNum; |
| } |
| |
| virtual void unsetHeap(int32_t seqNum) { |
| Parcel data, reply; |
| data.writeInterfaceToken(ICrypto::getInterfaceDescriptor()); |
| data.writeInt32(seqNum); |
| remote()->transact(UNSET_HEAP, data, &reply); |
| return; |
| } |
| |
| |
| private: |
| void readVector(Parcel &reply, Vector<uint8_t> &vector) const { |
| uint32_t size = reply.readInt32(); |
| vector.insertAt((size_t)0, size); |
| reply.read(vector.editArray(), size); |
| } |
| |
| void writeVector(Parcel &data, Vector<uint8_t> const &vector) const { |
| data.writeInt32(vector.size()); |
| data.write(vector.array(), vector.size()); |
| } |
| |
| DISALLOW_EVIL_CONSTRUCTORS(BpCrypto); |
| }; |
| |
| IMPLEMENT_META_INTERFACE(Crypto, "android.hardware.ICrypto"); |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| |
| void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const { |
| uint32_t size = data.readInt32(); |
| if (vector.insertAt((size_t)0, size) < 0) { |
| vector.clear(); |
| } |
| if (data.read(vector.editArray(), size) != NO_ERROR) { |
| vector.clear(); |
| android_errorWriteWithInfoLog(0x534e4554, "62872384", -1, NULL, 0); |
| } |
| } |
| |
| void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const { |
| reply->writeInt32(vector.size()); |
| reply->write(vector.array(), vector.size()); |
| } |
| |
| status_t BnCrypto::onTransact( |
| uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { |
| switch (code) { |
| case INIT_CHECK: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| reply->writeInt32(initCheck()); |
| |
| return OK; |
| } |
| |
| case IS_CRYPTO_SUPPORTED: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| uint8_t uuid[16]; |
| data.read(uuid, sizeof(uuid)); |
| reply->writeInt32(isCryptoSchemeSupported(uuid)); |
| |
| return OK; |
| } |
| |
| case CREATE_PLUGIN: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| |
| uint8_t uuid[16]; |
| data.read(uuid, sizeof(uuid)); |
| |
| size_t opaqueSize = data.readInt32(); |
| void *opaqueData = NULL; |
| |
| const size_t kMaxOpaqueSize = 100 * 1024; |
| if (opaqueSize > kMaxOpaqueSize) { |
| return BAD_VALUE; |
| } |
| |
| opaqueData = malloc(opaqueSize); |
| if (NULL == opaqueData) { |
| return NO_MEMORY; |
| } |
| |
| data.read(opaqueData, opaqueSize); |
| reply->writeInt32(createPlugin(uuid, opaqueData, opaqueSize)); |
| |
| free(opaqueData); |
| opaqueData = NULL; |
| |
| return OK; |
| } |
| |
| case DESTROY_PLUGIN: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| reply->writeInt32(destroyPlugin()); |
| |
| return OK; |
| } |
| |
| case REQUIRES_SECURE_COMPONENT: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| |
| const char *mime = data.readCString(); |
| if (mime == NULL) { |
| reply->writeInt32(BAD_VALUE); |
| } else { |
| reply->writeInt32(requiresSecureDecoderComponent(mime)); |
| } |
| |
| return OK; |
| } |
| |
| case DECRYPT: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| |
| CryptoPlugin::Mode mode = (CryptoPlugin::Mode)data.readInt32(); |
| CryptoPlugin::Pattern pattern; |
| pattern.mEncryptBlocks = data.readInt32(); |
| pattern.mSkipBlocks = data.readInt32(); |
| |
| uint8_t key[16]; |
| data.read(key, sizeof(key)); |
| |
| uint8_t iv[16]; |
| data.read(iv, sizeof(iv)); |
| |
| size_t totalSize = data.readInt32(); |
| |
| SourceBuffer source; |
| |
| source.mSharedMemory = |
| interface_cast<IMemory>(data.readStrongBinder()); |
| if (source.mSharedMemory == NULL) { |
| reply->writeInt32(BAD_VALUE); |
| return OK; |
| } |
| source.mHeapSeqNum = data.readInt32(); |
| |
| int32_t offset = data.readInt32(); |
| |
| int32_t numSubSamples = data.readInt32(); |
| if (numSubSamples < 0 || numSubSamples > 0xffff) { |
| reply->writeInt32(BAD_VALUE); |
| return OK; |
| } |
| |
| std::unique_ptr<CryptoPlugin::SubSample[]> subSamples = |
| std::make_unique<CryptoPlugin::SubSample[]>(numSubSamples); |
| |
| data.read(subSamples.get(), |
| sizeof(CryptoPlugin::SubSample) * numSubSamples); |
| |
| DestinationBuffer destination; |
| destination.mType = (DestinationType)data.readInt32(); |
| if (destination.mType == kDestinationTypeNativeHandle) { |
| destination.mHandle = data.readNativeHandle(); |
| if (destination.mHandle == NULL) { |
| reply->writeInt32(BAD_VALUE); |
| return OK; |
| } |
| } else if (destination.mType == kDestinationTypeSharedMemory) { |
| destination.mSharedMemory = |
| interface_cast<IMemory>(data.readStrongBinder()); |
| if (destination.mSharedMemory == NULL) { |
| reply->writeInt32(BAD_VALUE); |
| return OK; |
| } |
| sp<IMemory> dest = destination.mSharedMemory; |
| if (totalSize > dest->size() || |
| (size_t)dest->offset() > dest->size() - totalSize) { |
| reply->writeInt32(BAD_VALUE); |
| android_errorWriteLog(0x534e4554, "71389378"); |
| return OK; |
| } |
| } else { |
| reply->writeInt32(BAD_VALUE); |
| android_errorWriteLog(0x534e4554, "70526702"); |
| return OK; |
| } |
| |
| AString errorDetailMsg; |
| ssize_t result; |
| |
| size_t sumSubsampleSizes = 0; |
| bool overflow = false; |
| for (int32_t i = 0; i < numSubSamples; ++i) { |
| CryptoPlugin::SubSample &ss = subSamples[i]; |
| if (sumSubsampleSizes <= SIZE_MAX - ss.mNumBytesOfEncryptedData) { |
| sumSubsampleSizes += ss.mNumBytesOfEncryptedData; |
| } else { |
| overflow = true; |
| } |
| if (sumSubsampleSizes <= SIZE_MAX - ss.mNumBytesOfClearData) { |
| sumSubsampleSizes += ss.mNumBytesOfClearData; |
| } else { |
| overflow = true; |
| } |
| } |
| |
| if (overflow || sumSubsampleSizes != totalSize) { |
| result = -EINVAL; |
| } else if (totalSize > source.mSharedMemory->size()) { |
| result = -EINVAL; |
| } else if ((size_t)offset > source.mSharedMemory->size() - totalSize) { |
| result = -EINVAL; |
| } else { |
| result = decrypt(key, iv, mode, pattern, source, offset, |
| subSamples.get(), numSubSamples, destination, &errorDetailMsg); |
| } |
| |
| reply->writeInt32(result); |
| |
| if (isCryptoError(result)) { |
| reply->writeCString(errorDetailMsg.c_str()); |
| } |
| |
| if (destination.mType == kDestinationTypeNativeHandle) { |
| int err; |
| if ((err = native_handle_close(destination.mHandle)) < 0) { |
| ALOGW("secure buffer native_handle_close failed: %d", err); |
| } |
| if ((err = native_handle_delete(destination.mHandle)) < 0) { |
| ALOGW("secure buffer native_handle_delete failed: %d", err); |
| } |
| } |
| |
| subSamples.reset(); |
| return OK; |
| } |
| |
| case NOTIFY_RESOLUTION: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| |
| int32_t width = data.readInt32(); |
| int32_t height = data.readInt32(); |
| notifyResolution(width, height); |
| |
| return OK; |
| } |
| |
| case SET_MEDIADRM_SESSION: |
| { |
| CHECK_INTERFACE(IDrm, data, reply); |
| Vector<uint8_t> sessionId; |
| readVector(data, sessionId); |
| reply->writeInt32(setMediaDrmSession(sessionId)); |
| return OK; |
| } |
| |
| case SET_HEAP: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| sp<IMemoryHeap> heap = |
| interface_cast<IMemoryHeap>(data.readStrongBinder()); |
| reply->writeInt32(setHeap(heap)); |
| return OK; |
| } |
| |
| case UNSET_HEAP: |
| { |
| CHECK_INTERFACE(ICrypto, data, reply); |
| int32_t seqNum = data.readInt32(); |
| unsetHeap(seqNum); |
| return OK; |
| } |
| |
| default: |
| return BBinder::onTransact(code, data, reply, flags); |
| } |
| } |
| |
| } // namespace android |