blob: 80901f5753c022826da7d58e1b0183fb23da204e [file] [log] [blame]
/*
* Copyright (C) 2007 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.
*/
#include <drm_rights_manager.h>
#include <drm_inner.h>
#include <drm_file.h>
#include <drm_i18n.h>
static int32_t drm_getString(uint8_t* string, int32_t len, int32_t handle)
{
int32_t i;
for (i = 0; i < len; i++) {
if (DRM_FILE_FAILURE == DRM_file_read(handle, &string[i], 1))
return FALSE;
if (string[i] == '\n') {
string[i + 1] = '\0';
break;
}
}
return TRUE;
}
static int32_t drm_putString(uint8_t* string, int32_t handle)
{
int32_t i = 0;
for (i = 0;; i++) {
if (string[i] == '\0')
break;
if (DRM_FILE_FAILURE == DRM_file_write(handle, &string[i], 1))
return FALSE;
}
return TRUE;
}
static int32_t drm_writeToUidTxt(uint8_t* Uid, int32_t* id)
{
int32_t length;
int32_t i;
uint8_t idStr[8];
int32_t idMax;
uint8_t(*uidStr)[256];
uint16_t nameUcs2[MAX_FILENAME_LEN];
int32_t nameLen;
int32_t bytesConsumed;
int32_t handle;
int32_t fileRes;
if (*id < 1)
return FALSE;
/* convert in ucs2 */
nameLen = strlen(DRM_UID_FILE_PATH);
nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
(uint8_t *)DRM_UID_FILE_PATH,
nameLen,
nameUcs2,
MAX_FILENAME_LEN,
&bytesConsumed);
fileRes = DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
if (DRM_FILE_SUCCESS != fileRes) {
DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_WRITE,
&handle);
DRM_file_write(handle, (uint8_t *)"0\n", 2);
DRM_file_close(handle);
DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
}
if (!drm_getString(idStr, 8, handle)) {
DRM_file_close(handle);
return FALSE;
}
idMax = atoi((char *)idStr);
if (idMax < *id)
uidStr = malloc((idMax + 1) * 256);
else
uidStr = malloc(idMax * 256);
for (i = 0; i < idMax; i++) {
if (!drm_getString(uidStr[i], 256, handle)) {
DRM_file_close(handle);
free(uidStr);
return FALSE;
}
}
length = strlen((char *)Uid);
strcpy((char *)uidStr[*id - 1], (char *)Uid);
uidStr[*id - 1][length] = '\n';
uidStr[*id - 1][length + 1] = '\0';
if (idMax < (*id))
idMax++;
DRM_file_close(handle);
DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_WRITE,
&handle);
sprintf((char *)idStr, "%d", idMax);
if (!drm_putString(idStr, handle)) {
DRM_file_close(handle);
free(uidStr);
return FALSE;
}
if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t *)"\n", 1)) {
DRM_file_close(handle);
free(uidStr);
return FALSE;
}
for (i = 0; i < idMax; i++) {
if (!drm_putString(uidStr[i], handle)) {
DRM_file_close(handle);
free(uidStr);
return FALSE;
}
}
if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t *)"\n", 1)) {
DRM_file_close(handle);
free(uidStr);
return FALSE;
}
DRM_file_close(handle);
free(uidStr);
return TRUE;
}
/* See objmng_files.h */
int32_t drm_readFromUidTxt(uint8_t* Uid, int32_t* id, int32_t option)
{
int32_t i;
uint8_t p[256] = { 0 };
uint8_t idStr[8];
int32_t idMax = 0;
uint16_t nameUcs2[MAX_FILENAME_LEN];
int32_t nameLen = 0;
int32_t bytesConsumed;
int32_t handle;
int32_t fileRes;
if (NULL == id || NULL == Uid)
return FALSE;
DRM_file_startup();
/* convert in ucs2 */
nameLen = strlen(DRM_UID_FILE_PATH);
nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
(uint8_t *)DRM_UID_FILE_PATH,
nameLen,
nameUcs2,
MAX_FILENAME_LEN,
&bytesConsumed);
fileRes = DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
if (DRM_FILE_SUCCESS != fileRes) {
DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_WRITE,
&handle);
DRM_file_write(handle, (uint8_t *)"0\n", 2);
DRM_file_close(handle);
DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
}
if (!drm_getString(idStr, 8, handle)) {
DRM_file_close(handle);
return FALSE;
}
idMax = atoi((char *)idStr);
if (option == GET_UID) {
if (*id < 1 || *id > idMax) {
DRM_file_close(handle);
return FALSE;
}
for (i = 1; i <= *id; i++) {
if (!drm_getString(Uid, 256, handle)) {
DRM_file_close(handle);
return FALSE;
}
}
DRM_file_close(handle);
return TRUE;
}
if (option == GET_ID) {
*id = -1;
for (i = 1; i <= idMax; i++) {
if (!drm_getString(p, 256, handle)) {
DRM_file_close(handle);
return FALSE;
}
if (strstr((char *)p, (char *)Uid) != NULL
&& strlen((char *)p) == strlen((char *)Uid) + 1) {
*id = i;
DRM_file_close(handle);
return TRUE;
}
if ((*id == -1) && (strlen((char *)p) < 3))
*id = i;
}
if (*id != -1) {
DRM_file_close(handle);
return FALSE;
}
*id = idMax + 1;
DRM_file_close(handle);
return FALSE;
}
DRM_file_close(handle);
return FALSE;
}
static int32_t drm_acquireId(uint8_t* uid, int32_t* id)
{
if (TRUE == drm_readFromUidTxt(uid, id, GET_ID))
return TRUE;
drm_writeToUidTxt(uid, id);
return FALSE; /* The Uid is not exit, then return FALSE indicate it */
}
int32_t drm_writeOrReadInfo(int32_t id, T_DRM_Rights* Ro, int32_t* RoAmount, int32_t option)
{
uint8_t fullname[MAX_FILENAME_LEN] = {0};
int32_t tmpRoAmount;
uint16_t nameUcs2[MAX_FILENAME_LEN];
int32_t nameLen = 0;
int32_t bytesConsumed;
int32_t handle;
int32_t fileRes;
sprintf((char *)fullname, ANDROID_DRM_CORE_PATH"%d"EXTENSION_NAME_INFO, id);
/* convert in ucs2 */
nameLen = strlen((char *)fullname);
nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
fullname,
nameLen,
nameUcs2,
MAX_FILENAME_LEN,
&bytesConsumed);
fileRes = DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
if (DRM_FILE_SUCCESS != fileRes) {
if (GET_ALL_RO == option || GET_A_RO == option)
return FALSE;
if (GET_ROAMOUNT == option) {
*RoAmount = -1;
return TRUE;
}
}
DRM_file_close(handle);
DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ | DRM_FILE_MODE_WRITE,
&handle);
switch(option) {
case GET_ROAMOUNT:
if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)RoAmount, sizeof(int32_t))) {
DRM_file_close(handle);
return FALSE;
}
break;
case GET_ALL_RO:
DRM_file_setPosition(handle, sizeof(int32_t));
if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)Ro, (*RoAmount) * sizeof(T_DRM_Rights))) {
DRM_file_close(handle);
return FALSE;
}
break;
case SAVE_ALL_RO:
if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t*)RoAmount, sizeof(int32_t))) {
DRM_file_close(handle);
return FALSE;
}
if (NULL != Ro && *RoAmount >= 1) {
if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t*) Ro, (*RoAmount) * sizeof(T_DRM_Rights))) {
DRM_file_close(handle);
return FALSE;
}
}
break;
case GET_A_RO:
DRM_file_setPosition(handle, sizeof(int32_t) + (*RoAmount - 1) * sizeof(T_DRM_Rights));
if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)Ro, sizeof(T_DRM_Rights))) {
DRM_file_close(handle);
return FALSE;
}
break;
case SAVE_A_RO:
DRM_file_setPosition(handle, sizeof(int32_t) + (*RoAmount - 1) * sizeof(T_DRM_Rights));
if (DRM_FILE_FAILURE == DRM_file_write(handle, (uint8_t*)Ro, sizeof(T_DRM_Rights))) {
DRM_file_close(handle);
return FALSE;
}
DRM_file_setPosition(handle, 0);
if (DRM_FILE_FAILURE == DRM_file_read(handle, (uint8_t*)&tmpRoAmount, sizeof(int32_t))) {
DRM_file_close(handle);
return FALSE;
}
if (tmpRoAmount < *RoAmount) {
DRM_file_setPosition(handle, 0);
DRM_file_write(handle, (uint8_t*)RoAmount, sizeof(int32_t));
}
break;
default:
DRM_file_close(handle);
return FALSE;
}
DRM_file_close(handle);
return TRUE;
}
int32_t drm_appendRightsInfo(T_DRM_Rights* rights)
{
int32_t id;
int32_t roAmount;
if (NULL == rights)
return FALSE;
drm_acquireId(rights->uid, &id);
if (FALSE == drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT))
return FALSE;
if (-1 == roAmount)
roAmount = 0;
/* The RO amount increase */
roAmount++;
/* Save the rights information */
if (FALSE == drm_writeOrReadInfo(id, rights, &roAmount, SAVE_A_RO))
return FALSE;
return TRUE;
}
int32_t drm_getMaxIdFromUidTxt()
{
uint8_t idStr[8];
int32_t idMax = 0;
uint16_t nameUcs2[MAX_FILENAME_LEN] = {0};
int32_t nameLen = 0;
int32_t bytesConsumed;
int32_t handle;
int32_t fileRes;
/* convert in ucs2 */
nameLen = strlen(DRM_UID_FILE_PATH);
nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
(uint8_t *)DRM_UID_FILE_PATH,
nameLen,
nameUcs2,
MAX_FILENAME_LEN,
&bytesConsumed);
fileRes = DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
/* this means the uid.txt file is not exist, so there is not any DRM object */
if (DRM_FILE_SUCCESS != fileRes)
return 0;
if (!drm_getString(idStr, 8, handle)) {
DRM_file_close(handle);
return -1;
}
DRM_file_close(handle);
idMax = atoi((char *)idStr);
return idMax;
}
int32_t drm_removeIdInfoFile(int32_t id)
{
uint8_t filename[MAX_FILENAME_LEN] = {0};
uint16_t nameUcs2[MAX_FILENAME_LEN];
int32_t nameLen = 0;
int32_t bytesConsumed;
if (id <= 0)
return FALSE;
sprintf((char *)filename, ANDROID_DRM_CORE_PATH"%d"EXTENSION_NAME_INFO, id);
/* convert in ucs2 */
nameLen = strlen((char *)filename);
nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
filename,
nameLen,
nameUcs2,
MAX_FILENAME_LEN,
&bytesConsumed);
if (DRM_FILE_SUCCESS != DRM_file_delete(nameUcs2, nameLen))
return FALSE;
return TRUE;
}
int32_t drm_updateUidTxtWhenDelete(int32_t id)
{
uint16_t nameUcs2[MAX_FILENAME_LEN];
int32_t nameLen = 0;
int32_t bytesConsumed;
int32_t handle;
int32_t fileRes;
int32_t bufferLen;
uint8_t *buffer;
uint8_t idStr[8];
int32_t idMax;
if (id <= 0)
return FALSE;
nameLen = strlen(DRM_UID_FILE_PATH);
nameLen = DRM_i18n_mbsToWcs(DRM_CHARSET_UTF8,
(uint8_t *)DRM_UID_FILE_PATH,
nameLen,
nameUcs2,
MAX_FILENAME_LEN,
&bytesConsumed);
bufferLen = DRM_file_getFileLength(nameUcs2, nameLen);
if (bufferLen <= 0)
return FALSE;
buffer = (uint8_t *)malloc(bufferLen);
if (NULL == buffer)
return FALSE;
fileRes = DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_READ,
&handle);
if (DRM_FILE_SUCCESS != fileRes) {
free(buffer);
return FALSE;
}
drm_getString(idStr, 8, handle);
idMax = atoi((char *)idStr);
bufferLen -= strlen((char *)idStr);
fileRes = DRM_file_read(handle, buffer, bufferLen);
buffer[bufferLen] = '\0';
DRM_file_close(handle);
/* handle this buffer */
{
uint8_t *pStart, *pEnd;
int32_t i, movLen;
pStart = buffer;
pEnd = pStart;
for (i = 0; i < id; i++) {
if (pEnd != pStart)
pStart = ++pEnd;
while ('\n' != *pEnd)
pEnd++;
if (pStart == pEnd)
pStart--;
}
movLen = bufferLen - (pEnd - buffer);
memmove(pStart, pEnd, movLen);
bufferLen -= (pEnd - pStart);
}
if (DRM_FILE_SUCCESS != DRM_file_delete(nameUcs2, nameLen)) {
free(buffer);
return FALSE;
}
fileRes = DRM_file_open(nameUcs2,
nameLen,
DRM_FILE_MODE_WRITE,
&handle);
if (DRM_FILE_SUCCESS != fileRes) {
free(buffer);
return FALSE;
}
sprintf((char *)idStr, "%d", idMax);
drm_putString(idStr, handle);
DRM_file_write(handle, (uint8_t*)"\n", 1);
DRM_file_write(handle, buffer, bufferLen);
free(buffer);
DRM_file_close(handle);
return TRUE;
}
int32_t drm_getKey(uint8_t* uid, uint8_t* KeyValue)
{
T_DRM_Rights ro;
int32_t id, roAmount;
if (NULL == uid || NULL == KeyValue)
return FALSE;
if (FALSE == drm_readFromUidTxt(uid, &id, GET_ID))
return FALSE;
if (FALSE == drm_writeOrReadInfo(id, NULL, &roAmount, GET_ROAMOUNT))
return FALSE;
if (roAmount <= 0)
return FALSE;
memset(&ro, 0, sizeof(T_DRM_Rights));
roAmount = 1;
if (FALSE == drm_writeOrReadInfo(id, &ro, &roAmount, GET_A_RO))
return FALSE;
memcpy(KeyValue, ro.KeyValue, DRM_KEY_LEN);
return TRUE;
}
void drm_discardPaddingByte(uint8_t *decryptedBuf, int32_t *decryptedBufLen)
{
int32_t tmpLen = *decryptedBufLen;
int32_t i;
if (NULL == decryptedBuf || *decryptedBufLen < 0)
return;
/* Check whether the last several bytes are padding or not */
for (i = 1; i < decryptedBuf[tmpLen - 1]; i++) {
if (decryptedBuf[tmpLen - 1 - i] != decryptedBuf[tmpLen - 1])
break; /* Not the padding bytes */
}
if (i == decryptedBuf[tmpLen - 1]) /* They are padding bytes */
*decryptedBufLen = tmpLen - i;
return;
}
int32_t drm_aesDecBuffer(uint8_t * Buffer, int32_t * BufferLen, aes_decrypt_ctx ctx[1])
{
uint8_t dbuf[3 * DRM_ONE_AES_BLOCK_LEN], buf[DRM_ONE_AES_BLOCK_LEN];
uint64_t i, len, wlen = DRM_ONE_AES_BLOCK_LEN, curLen, restLen;
uint8_t *pTarget, *pTargetHead;
pTargetHead = Buffer;
pTarget = Buffer;
curLen = 0;
restLen = *BufferLen;
if (restLen > 2 * DRM_ONE_AES_BLOCK_LEN) {
len = 2 * DRM_ONE_AES_BLOCK_LEN;
} else {
len = restLen;
}
memcpy(dbuf, Buffer, (size_t)len);
restLen -= len;
Buffer += len;
if (len < 2 * DRM_ONE_AES_BLOCK_LEN) { /* The original file is less than one block in length */
len -= DRM_ONE_AES_BLOCK_LEN;
/* Decrypt from position len to position len + DRM_ONE_AES_BLOCK_LEN */
aes_decrypt((dbuf + len), (dbuf + len), ctx);
/* Undo the CBC chaining */
for (i = 0; i < len; ++i)
dbuf[i] ^= dbuf[i + DRM_ONE_AES_BLOCK_LEN];
/* Output the decrypted bytes */
memcpy(pTarget, dbuf, (size_t)len);
pTarget += len;
} else {
uint8_t *b1 = dbuf, *b2 = b1 + DRM_ONE_AES_BLOCK_LEN, *b3 = b2 + DRM_ONE_AES_BLOCK_LEN, *bt;
for (;;) { /* While some ciphertext remains, prepare to decrypt block b2 */
/* Read in the next block to see if ciphertext stealing is needed */
b3 = Buffer;
if (restLen > DRM_ONE_AES_BLOCK_LEN) {
len = DRM_ONE_AES_BLOCK_LEN;
} else {
len = restLen;
}
restLen -= len;
Buffer += len;
/* Decrypt the b2 block */
aes_decrypt((uint8_t *)b2, buf, ctx);
if (len == 0 || len == DRM_ONE_AES_BLOCK_LEN) { /* No ciphertext stealing */
/* Unchain CBC using the previous ciphertext block in b1 */
for (i = 0; i < DRM_ONE_AES_BLOCK_LEN; ++i)
buf[i] ^= b1[i];
} else { /* Partial last block - use ciphertext stealing */
wlen = len;
/* Produce last 'len' bytes of plaintext by xoring with */
/* The lowest 'len' bytes of next block b3 - C[N-1] */
for (i = 0; i < len; ++i)
buf[i] ^= b3[i];
/* Reconstruct the C[N-1] block in b3 by adding in the */
/* Last (DRM_ONE_AES_BLOCK_LEN - len) bytes of C[N-2] in b2 */
for (i = len; i < DRM_ONE_AES_BLOCK_LEN; ++i)
b3[i] = buf[i];
/* Decrypt the C[N-1] block in b3 */
aes_decrypt((uint8_t *)b3, (uint8_t *)b3, ctx);
/* Produce the last but one plaintext block by xoring with */
/* The last but two ciphertext block */
for (i = 0; i < DRM_ONE_AES_BLOCK_LEN; ++i)
b3[i] ^= b1[i];
/* Write decrypted plaintext blocks */
memcpy(pTarget, b3, DRM_ONE_AES_BLOCK_LEN);
pTarget += DRM_ONE_AES_BLOCK_LEN;
}
/* Write the decrypted plaintext block */
memcpy(pTarget, buf, (size_t)wlen);
pTarget += wlen;
if (len != DRM_ONE_AES_BLOCK_LEN) {
*BufferLen = pTarget - pTargetHead;
return 0;
}
/* Advance the buffer pointers */
bt = b1, b1 = b2, b2 = b3, b3 = bt;
}
}
return 0;
}
int32_t drm_updateDcfDataLen(uint8_t* pDcfLastData, uint8_t* keyValue, int32_t* moreBytes)
{
aes_decrypt_ctx ctx[1];
int32_t len = DRM_TWO_AES_BLOCK_LEN;
if (NULL == pDcfLastData || NULL == keyValue)
return FALSE;
aes_decrypt_key128(keyValue, ctx);
if (drm_aesDecBuffer(pDcfLastData, &len, ctx) < 0)
return FALSE;
drm_discardPaddingByte(pDcfLastData, &len);
*moreBytes = DRM_TWO_AES_BLOCK_LEN - len;
return TRUE;
}