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