Home | History | Annotate | Download | only in common
      1 /*
      2  * Copyright (C) 2010 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 #include <assert.h>
     18 #include <errno.h>
     19 #include <fcntl.h>
     20 #include <pthread.h>
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <sys/stat.h>
     24 #include <unistd.h>
     25 #include <openssl/aes.h>
     26 
     27 #include "FwdLockGlue.h"
     28 
     29 #define TRUE 1
     30 #define FALSE 0
     31 
     32 #define KEY_SIZE 16
     33 #define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
     34 
     35 static int isInitialized = FALSE;
     36 
     37 static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";
     38 
     39 static AES_KEY encryptionRoundKeys;
     40 static AES_KEY decryptionRoundKeys;
     41 
     42 /**
     43  * Creates all directories along the fully qualified path of the given file.
     44  *
     45  * @param[in] path A reference to the fully qualified path of a file.
     46  * @param[in] mode The access mode to use for the directories being created.
     47  *
     48  * @return A Boolean value indicating whether the operation was successful.
     49  */
     50 static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) {
     51     int result = TRUE;
     52     size_t partialPathLength = strlen(path);
     53     char *partialPath = malloc(partialPathLength + 1);
     54     if (partialPath == NULL) {
     55         result = FALSE;
     56     } else {
     57         size_t i;
     58         for (i = 0; i < partialPathLength; ++i) {
     59             if (path[i] == '/' && i > 0) {
     60                 partialPath[i] = '\0';
     61                 if (mkdir(partialPath, mode) != 0 && errno != EEXIST) {
     62                     result = FALSE;
     63                     break;
     64                 }
     65             }
     66             partialPath[i] = path[i];
     67         }
     68         free(partialPath);
     69     }
     70     return result;
     71 }
     72 
     73 /**
     74  * Initializes the round keys used for encryption and decryption of session keys. First creates a
     75  * device-unique key-encryption key if none exists yet.
     76  */
     77 static void FwdLockGlue_InitializeRoundKeys() {
     78     unsigned char keyEncryptionKey[KEY_SIZE];
     79     int fileDesc = open(strKeyFilename, O_RDONLY);
     80     if (fileDesc >= 0) {
     81         if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
     82             isInitialized = TRUE;
     83         }
     84         (void)close(fileDesc);
     85     } else if (errno == ENOENT &&
     86                FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) &&
     87                FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) {
     88         fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR);
     89         if (fileDesc >= 0) {
     90             if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
     91                 isInitialized = TRUE;
     92             }
     93             (void)close(fileDesc);
     94         }
     95     }
     96     if (isInitialized) {
     97         if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 ||
     98             AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) {
     99             isInitialized = FALSE;
    100         }
    101     }
    102     memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
    103 }
    104 
    105 /**
    106  * Validates the padding of a decrypted key.
    107  *
    108  * @param[in] pData A reference to the buffer containing the decrypted key and padding.
    109  * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
    110  *
    111  * @return A Boolean value indicating whether the padding was valid.
    112  */
    113 static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) {
    114     size_t i;
    115     size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE);
    116     pData += decryptedKeyLength;
    117     for (i = 0; i < padding; ++i) {
    118         if ((size_t)*pData != padding) {
    119             return FALSE;
    120         }
    121         ++pData;
    122     }
    123     return TRUE;
    124 }
    125 
    126 int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) {
    127     // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the
    128     // non-blocking version of "/dev/random").
    129     ssize_t numBytesRead = 0;
    130     int fileDesc = open("/dev/urandom", O_RDONLY);
    131     if (fileDesc >= 0) {
    132         numBytesRead = read(fileDesc, pBuffer, numBytes);
    133         (void)close(fileDesc);
    134     }
    135     return numBytesRead >= 0 && (size_t)numBytesRead == numBytes;
    136 }
    137 
    138 int FwdLockGlue_InitializeKeyEncryption() {
    139     static pthread_once_t once = PTHREAD_ONCE_INIT;
    140     pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
    141     return isInitialized;
    142 }
    143 
    144 size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
    145     return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
    146 }
    147 
    148 int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
    149                            size_t plaintextKeyLength,
    150                            void *pEncryptedKey,
    151                            size_t encryptedKeyLength) {
    152     int result = FALSE;
    153     assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength));
    154     if (FwdLockGlue_InitializeKeyEncryption()) {
    155         unsigned char initVector[AES_BLOCK_SIZE];
    156         if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) {
    157             size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE);
    158             size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
    159             memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength);
    160             memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding);
    161             memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE);
    162             AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys,
    163                             initVector, AES_ENCRYPT);
    164             result = TRUE;
    165         }
    166     }
    167     return result;
    168 }
    169 
    170 int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
    171                            size_t encryptedKeyLength,
    172                            void *pDecryptedKey,
    173                            size_t decryptedKeyLength) {
    174     int result = FALSE;
    175     assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength));
    176     if (FwdLockGlue_InitializeKeyEncryption()) {
    177         size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
    178         unsigned char *pData = malloc(dataLength);
    179         if (pData != NULL) {
    180             unsigned char initVector[AES_BLOCK_SIZE];
    181             memcpy(pData, pEncryptedKey, dataLength);
    182             memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE);
    183             AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector,
    184                             AES_DECRYPT);
    185             memcpy(pDecryptedKey, pData, decryptedKeyLength);
    186             result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength);
    187             free(pData);
    188         }
    189     }
    190     return result;
    191 }
    192