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
35static int isInitialized = FALSE;
36
37static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";
38
39static AES_KEY encryptionRoundKeys;
40static 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 */
50static 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 */
77static 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 */
113static 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
126int 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
138int FwdLockGlue_InitializeKeyEncryption() {
139    static pthread_once_t once = PTHREAD_ONCE_INIT;
140    pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
141    return isInitialized;
142}
143
144size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
145    return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
146}
147
148int 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
170int 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