summaryrefslogtreecommitdiffstats
path: root/drm/libdrmframework/plugins/forward-lock/internal-format/common/FwdLockGlue.c
blob: 92bda8ffadb1d676b3ddad148abab7b3da010410 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * Copyright (C) 2010 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 <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <openssl/aes.h>

#include "FwdLockGlue.h"

#define TRUE 1
#define FALSE 0

#define KEY_SIZE 16
#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)

static int isInitialized = FALSE;

static const char strKeyFilename[] = "/data/drm/fwdlock/kek.dat";

static AES_KEY encryptionRoundKeys;
static AES_KEY decryptionRoundKeys;

/**
 * Creates all directories along the fully qualified path of the given file.
 *
 * @param[in] path A reference to the fully qualified path of a file.
 * @param[in] mode The access mode to use for the directories being created.
 *
 * @return A Boolean value indicating whether the operation was successful.
 */
static int FwdLockGlue_CreateDirectories(const char *path, mode_t mode) {
    int result = TRUE;
    size_t partialPathLength = strlen(path);
    char *partialPath = malloc(partialPathLength + 1);
    if (partialPath == NULL) {
        result = FALSE;
    } else {
        size_t i;
        for (i = 0; i < partialPathLength; ++i) {
            if (path[i] == '/' && i > 0) {
                partialPath[i] = '\0';
                if (mkdir(partialPath, mode) != 0 && errno != EEXIST) {
                    result = FALSE;
                    break;
                }
            }
            partialPath[i] = path[i];
        }
        free(partialPath);
    }
    return result;
}

/**
 * Initializes the round keys used for encryption and decryption of session keys. First creates a
 * device-unique key-encryption key if none exists yet.
 */
static void FwdLockGlue_InitializeRoundKeys() {
    unsigned char keyEncryptionKey[KEY_SIZE];
    int fileDesc = open(strKeyFilename, O_RDONLY);
    if (fileDesc >= 0) {
        if (read(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
            isInitialized = TRUE;
        }
        (void)close(fileDesc);
    } else if (errno == ENOENT &&
               FwdLockGlue_GetRandomNumber(keyEncryptionKey, KEY_SIZE) &&
               FwdLockGlue_CreateDirectories(strKeyFilename, S_IRWXU)) {
        fileDesc = open(strKeyFilename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR);
        if (fileDesc >= 0) {
            if (write(fileDesc, keyEncryptionKey, KEY_SIZE) == KEY_SIZE) {
                isInitialized = TRUE;
            }
            (void)close(fileDesc);
        }
    }
    if (isInitialized) {
        if (AES_set_encrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &encryptionRoundKeys) != 0 ||
            AES_set_decrypt_key(keyEncryptionKey, KEY_SIZE_IN_BITS, &decryptionRoundKeys) != 0) {
            isInitialized = FALSE;
        }
    }
    memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
}

/**
 * Validates the padding of a decrypted key.
 *
 * @param[in] pData A reference to the buffer containing the decrypted key and padding.
 * @param[in] decryptedKeyLength The length in bytes of the decrypted key.
 *
 * @return A Boolean value indicating whether the padding was valid.
 */
static int FwdLockGlue_ValidatePadding(const unsigned char *pData, size_t decryptedKeyLength) {
    size_t i;
    size_t padding = AES_BLOCK_SIZE - (decryptedKeyLength % AES_BLOCK_SIZE);
    pData += decryptedKeyLength;
    for (i = 0; i < padding; ++i) {
        if ((size_t)*pData != padding) {
            return FALSE;
        }
        ++pData;
    }
    return TRUE;
}

int FwdLockGlue_GetRandomNumber(void *pBuffer, size_t numBytes) {
    // Generate 'cryptographically secure' random bytes by reading them from "/dev/urandom" (the
    // non-blocking version of "/dev/random").
    ssize_t numBytesRead = 0;
    int fileDesc = open("/dev/urandom", O_RDONLY);
    if (fileDesc >= 0) {
        numBytesRead = read(fileDesc, pBuffer, numBytes);
        (void)close(fileDesc);
    }
    return numBytesRead >= 0 && (size_t)numBytesRead == numBytes;
}

int FwdLockGlue_InitializeKeyEncryption() {
    static pthread_once_t once = PTHREAD_ONCE_INIT;
    pthread_once(&once, FwdLockGlue_InitializeRoundKeys);
    return isInitialized;
}

size_t FwdLockGlue_GetEncryptedKeyLength(size_t plaintextKeyLength) {
    return ((plaintextKeyLength / AES_BLOCK_SIZE) + 2) * AES_BLOCK_SIZE;
}

int FwdLockGlue_EncryptKey(const void *pPlaintextKey,
                           size_t plaintextKeyLength,
                           void *pEncryptedKey,
                           size_t encryptedKeyLength) {
    int result = FALSE;
    assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(plaintextKeyLength));
    if (FwdLockGlue_InitializeKeyEncryption()) {
        unsigned char initVector[AES_BLOCK_SIZE];
        if (FwdLockGlue_GetRandomNumber(initVector, AES_BLOCK_SIZE)) {
            size_t padding = AES_BLOCK_SIZE - (plaintextKeyLength % AES_BLOCK_SIZE);
            size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
            memcpy(pEncryptedKey, pPlaintextKey, plaintextKeyLength);
            memset((unsigned char *)pEncryptedKey + plaintextKeyLength, (int)padding, padding);
            memcpy((unsigned char *)pEncryptedKey + dataLength, initVector, AES_BLOCK_SIZE);
            AES_cbc_encrypt(pEncryptedKey, pEncryptedKey, dataLength, &encryptionRoundKeys,
                            initVector, AES_ENCRYPT);
            result = TRUE;
        }
    }
    return result;
}

int FwdLockGlue_DecryptKey(const void *pEncryptedKey,
                           size_t encryptedKeyLength,
                           void *pDecryptedKey,
                           size_t decryptedKeyLength) {
    int result = FALSE;
    assert(encryptedKeyLength == FwdLockGlue_GetEncryptedKeyLength(decryptedKeyLength));
    if (FwdLockGlue_InitializeKeyEncryption()) {
        size_t dataLength = encryptedKeyLength - AES_BLOCK_SIZE;
        unsigned char *pData = malloc(dataLength);
        if (pData != NULL) {
            unsigned char initVector[AES_BLOCK_SIZE];
            memcpy(pData, pEncryptedKey, dataLength);
            memcpy(initVector, (const unsigned char *)pEncryptedKey + dataLength, AES_BLOCK_SIZE);
            AES_cbc_encrypt(pData, pData, dataLength, &decryptionRoundKeys, initVector,
                            AES_DECRYPT);
            memcpy(pDecryptedKey, pData, decryptedKeyLength);
            result = FwdLockGlue_ValidatePadding(pData, decryptedKeyLength);
            free(pData);
        }
    }
    return result;
}