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  */
FwdLockGlue_CreateDirectories(const char * path,mode_t mode)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  */
FwdLockGlue_InitializeRoundKeys()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  */
FwdLockGlue_ValidatePadding(const unsigned char * pData,size_t decryptedKeyLength)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 
FwdLockGlue_GetRandomNumber(void * pBuffer,size_t numBytes)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 
FwdLockGlue_InitializeKeyEncryption()138 int FwdLockGlue_InitializeKeyEncryption() {
139     static pthread_once_t once = PTHREAD_ONCE_INIT;
140     pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
141     return isInitialized;
142 }
143 
FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength)144 size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
145     return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
146 }
147 
FwdLockGlue_EncryptKey(const void * pPlaintextKey,size_t plaintextKeyLength,void * pEncryptedKey,size_t encryptedKeyLength)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 
FwdLockGlue_DecryptKey(const void * pEncryptedKey,size_t encryptedKeyLength,void * pDecryptedKey,size_t decryptedKeyLength)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