FwdLockFile.c revision 90855078eb989944bca1824058d7231cd68e5021
1cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/*
2cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Copyright (C) 2010 The Android Open Source Project
3cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
4cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Licensed under the Apache License, Version 2.0 (the "License");
5cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * you may not use this file except in compliance with the License.
6cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * You may obtain a copy of the License at
7cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
8cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *      http://www.apache.org/licenses/LICENSE-2.0
9cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
10cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Unless required by applicable law or agreed to in writing, software
11cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * distributed under the License is distributed on an "AS IS" BASIS,
12cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * See the License for the specific language governing permissions and
14cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * limitations under the License.
15cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
16cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
17cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <assert.h>
18cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <errno.h>
19cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <fcntl.h>
20255f89faee13dc491cb64fbeae3c763e7e2ea4e6Chandler Carruth#include <limits.h>
21cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <pthread.h>
22cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <stdlib.h>
23cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <string.h>
24cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <unistd.h>
25cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <openssl/aes.h>
26cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include <openssl/hmac.h>
27cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
28cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include "FwdLockFile.h"
29cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#include "FwdLockGlue.h"
30cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
31cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define TRUE 1
32cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define FALSE 0
33cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
34cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define INVALID_OFFSET ((off64_t)-1)
35cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
36cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define INVALID_BLOCK_INDEX ((uint64_t)-1)
37cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
38cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define MAX_NUM_SESSIONS 128
39cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
40cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define KEY_SIZE AES_BLOCK_SIZE
41cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define KEY_SIZE_IN_BITS (KEY_SIZE * 8)
42cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
43cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define SHA1_HASH_SIZE 20
44cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define SHA1_BLOCK_SIZE 64
45cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
46cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define FWD_LOCK_VERSION 0
47cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define FWD_LOCK_SUBFORMAT 0
48cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define USAGE_RESTRICTION_FLAGS 0
49cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define CONTENT_TYPE_LENGTH_POS 7
50cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define TOP_HEADER_SIZE 8
51cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
52cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem#define SIG_CALC_BUFFER_SIZE (16 * SHA1_BLOCK_SIZE)
53cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
54cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/**
55cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Data type for the per-file state information needed by the decoder.
56cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
57cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemtypedef struct FwdLockFile_Session {
58cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    int fileDesc;
59cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    unsigned char topHeader[TOP_HEADER_SIZE];
60cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    char *pContentType;
61cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    size_t contentTypeLength;
62cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    void *pEncryptedSessionKey;
63cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    size_t encryptedSessionKeyLength;
64cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    unsigned char dataSignature[SHA1_HASH_SIZE];
65cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    unsigned char headerSignature[SHA1_HASH_SIZE];
66cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    off64_t dataOffset;
67cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    off64_t filePos;
68cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    AES_KEY encryptionRoundKeys;
69cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    HMAC_CTX signingContext;
70cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    unsigned char keyStream[AES_BLOCK_SIZE];
71cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    uint64_t blockIndex;
72cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem} FwdLockFile_Session_t;
73cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
74cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic FwdLockFile_Session_t *sessionPtrs[MAX_NUM_SESSIONS] = { NULL };
75cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
76cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic pthread_mutex_t sessionAcquisitionMutex = PTHREAD_MUTEX_INITIALIZER;
77cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
78cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic const unsigned char topHeaderTemplate[] =
79cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    { 'F', 'W', 'L', 'K', FWD_LOCK_VERSION, FWD_LOCK_SUBFORMAT, USAGE_RESTRICTION_FLAGS };
80cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
81cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/**
82cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Acquires an unused file session for the given file descriptor.
83cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
84cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[in] fileDesc A file descriptor.
85cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
86cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @return A session ID.
87cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
88cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic int FwdLockFile_AcquireSession(int fileDesc) {
89cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    int sessionId = -1;
90cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    if (fileDesc < 0) {
91cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        errno = EBADF;
92cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    } else {
93cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        int i;
94cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        pthread_mutex_lock(&sessionAcquisitionMutex);
95cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
96cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
97cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            if (sessionPtrs[candidateSessionId] == NULL) {
98cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                sessionPtrs[candidateSessionId] = malloc(sizeof **sessionPtrs);
99cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                if (sessionPtrs[candidateSessionId] != NULL) {
100cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    sessionPtrs[candidateSessionId]->fileDesc = fileDesc;
101cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    sessionPtrs[candidateSessionId]->pContentType = NULL;
102cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    sessionPtrs[candidateSessionId]->pEncryptedSessionKey = NULL;
103cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    sessionId = candidateSessionId;
104cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                }
105cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                break;
106cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            }
107cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        }
108cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        pthread_mutex_unlock(&sessionAcquisitionMutex);
109cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        if (i == MAX_NUM_SESSIONS) {
110cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            errno = ENFILE;
111cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        }
112cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    }
113cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    return sessionId;
114cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem}
115cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
116cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/**
117cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Finds the file session associated with the given file descriptor.
118cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
119cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[in] fileDesc A file descriptor.
120cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
121cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @return A session ID.
122cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
123cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic int FwdLockFile_FindSession(int fileDesc) {
124cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    int sessionId = -1;
125cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    if (fileDesc < 0) {
126cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        errno = EBADF;
127cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    } else {
128cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        int i;
129cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        pthread_mutex_lock(&sessionAcquisitionMutex);
130cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        for (i = 0; i < MAX_NUM_SESSIONS; ++i) {
131cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            int candidateSessionId = (fileDesc + i) % MAX_NUM_SESSIONS;
132cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            if (sessionPtrs[candidateSessionId] != NULL &&
133cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                sessionPtrs[candidateSessionId]->fileDesc == fileDesc) {
134cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                sessionId = candidateSessionId;
135cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                break;
136cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            }
137cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        }
138cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        pthread_mutex_unlock(&sessionAcquisitionMutex);
139cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        if (i == MAX_NUM_SESSIONS) {
140cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            errno = EBADF;
141cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        }
142cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    }
143cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    return sessionId;
144cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem}
145cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
146cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/**
147cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Releases a file session.
148cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
149cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[in] sessionID A session ID.
150cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
151cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic void FwdLockFile_ReleaseSession(int sessionId) {
152cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    pthread_mutex_lock(&sessionAcquisitionMutex);
153cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    assert(0 <= sessionId && sessionId < MAX_NUM_SESSIONS && sessionPtrs[sessionId] != NULL);
154cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    free(sessionPtrs[sessionId]->pContentType);
155cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    free(sessionPtrs[sessionId]->pEncryptedSessionKey);
156cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    memset(sessionPtrs[sessionId], 0, sizeof *sessionPtrs[sessionId]); // Zero out key data.
157cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    free(sessionPtrs[sessionId]);
158cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    sessionPtrs[sessionId] = NULL;
159cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    pthread_mutex_unlock(&sessionAcquisitionMutex);
160cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem}
161cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
162cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/**
163cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Derives keys for encryption and signing from the encrypted session key.
164cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
165cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[in,out] pSession A reference to a file session.
166cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
167cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @return A Boolean value indicating whether key derivation was successful.
168cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
169cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic int FwdLockFile_DeriveKeys(FwdLockFile_Session_t * pSession) {
170cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    int result;
171cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    struct FwdLockFile_DeriveKeys_Data {
172cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        AES_KEY sessionRoundKeys;
173cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        unsigned char value[KEY_SIZE];
174cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        unsigned char key[KEY_SIZE];
175cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    } *pData = malloc(sizeof *pData);
176cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    if (pData == NULL) {
177cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        result = FALSE;
178cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    } else {
179cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        result = FwdLockGlue_DecryptKey(pSession->pEncryptedSessionKey,
180cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                                        pSession->encryptedSessionKeyLength, pData->key, KEY_SIZE);
181cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        if (result) {
182cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS, &pData->sessionRoundKeys) != 0) {
183cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                result = FALSE;
184cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            } else {
185cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                // Encrypt the 16-byte value {0, 0, ..., 0} to produce the encryption key.
186cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                memset(pData->value, 0, KEY_SIZE);
187cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
188cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                if (AES_set_encrypt_key(pData->key, KEY_SIZE_IN_BITS,
189cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                                        &pSession->encryptionRoundKeys) != 0) {
190cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    result = FALSE;
191cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                } else {
192cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    // Encrypt the 16-byte value {1, 0, ..., 0} to produce the signing key.
193cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    ++pData->value[0];
194cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    AES_encrypt(pData->value, pData->key, &pData->sessionRoundKeys);
195cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    HMAC_CTX_init(&pSession->signingContext);
196cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                    HMAC_Init_ex(&pSession->signingContext, pData->key, KEY_SIZE, EVP_sha1(), NULL);
197cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                }
198cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            }
199cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        }
200cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        if (!result) {
201cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem            errno = ENOSYS;
202cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        }
203cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        memset(pData, 0, sizeof pData); // Zero out key data.
204cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        free(pData);
205cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    }
206cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    return result;
207cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem}
208cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem
209cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem/**
210cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * Calculates the counter, treated as a 16-byte little-endian number, used to generate the keystream
211cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * for the given block.
212cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem *
213cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[in] pNonce A reference to the nonce.
214cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[in] blockIndex The index number of the block.
215cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem * @param[out] pCounter A reference to the counter.
216cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem */
217cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotemstatic void FwdLockFile_CalculateCounter(const unsigned char *pNonce,
218cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                                         uint64_t blockIndex,
219cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem                                         unsigned char *pCounter) {
220cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    unsigned char carry = 0;
221cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    size_t i = 0;
222cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    for (; i < sizeof blockIndex; ++i) {
223cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        unsigned char part = pNonce[i] + (unsigned char)(blockIndex >> (i * CHAR_BIT));
224cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        pCounter[i] = part + carry;
225cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem        carry = (part < pNonce[i] || pCounter[i] < part) ? 1 : 0;
226cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    }
227cbd9a19b5d6ff93efa82c467508ede78b8af3bacNadav Rotem    for (; i < AES_BLOCK_SIZE; ++i) {
228        pCounter[i] = pNonce[i] + carry;
229        carry = (pCounter[i] < pNonce[i]) ? 1 : 0;
230    }
231}
232
233/**
234 * Decrypts the byte at the current file position using AES-128-CTR. In CTR (or "counter") mode,
235 * encryption and decryption are performed using the same algorithm.
236 *
237 * @param[in,out] pSession A reference to a file session.
238 * @param[in] pByte The byte to decrypt.
239 */
240void FwdLockFile_DecryptByte(FwdLockFile_Session_t * pSession, unsigned char *pByte) {
241    uint64_t blockIndex = pSession->filePos / AES_BLOCK_SIZE;
242    uint64_t blockOffset = pSession->filePos % AES_BLOCK_SIZE;
243    if (blockIndex != pSession->blockIndex) {
244        // The first 16 bytes of the encrypted session key is used as the nonce.
245        unsigned char counter[AES_BLOCK_SIZE];
246        FwdLockFile_CalculateCounter(pSession->pEncryptedSessionKey, blockIndex, counter);
247        AES_encrypt(counter, pSession->keyStream, &pSession->encryptionRoundKeys);
248        pSession->blockIndex = blockIndex;
249    }
250    *pByte ^= pSession->keyStream[blockOffset];
251}
252
253int FwdLockFile_attach(int fileDesc) {
254    int sessionId = FwdLockFile_AcquireSession(fileDesc);
255    if (sessionId >= 0) {
256        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
257        int isSuccess = FALSE;
258        if (read(fileDesc, pSession->topHeader, TOP_HEADER_SIZE) == TOP_HEADER_SIZE &&
259                memcmp(pSession->topHeader, topHeaderTemplate, sizeof topHeaderTemplate) == 0) {
260            pSession->contentTypeLength = pSession->topHeader[CONTENT_TYPE_LENGTH_POS];
261            assert(pSession->contentTypeLength <= UCHAR_MAX); // Untaint scalar for code checkers.
262            pSession->pContentType = malloc(pSession->contentTypeLength + 1);
263            if (pSession->pContentType != NULL &&
264                    read(fileDesc, pSession->pContentType, pSession->contentTypeLength) ==
265                            (ssize_t)pSession->contentTypeLength) {
266                pSession->pContentType[pSession->contentTypeLength] = '\0';
267                pSession->encryptedSessionKeyLength = FwdLockGlue_GetEncryptedKeyLength(KEY_SIZE);
268                pSession->pEncryptedSessionKey = malloc(pSession->encryptedSessionKeyLength);
269                if (pSession->pEncryptedSessionKey != NULL &&
270                        read(fileDesc, pSession->pEncryptedSessionKey,
271                             pSession->encryptedSessionKeyLength) ==
272                                (ssize_t)pSession->encryptedSessionKeyLength &&
273                        read(fileDesc, pSession->dataSignature, SHA1_HASH_SIZE) ==
274                                SHA1_HASH_SIZE &&
275                        read(fileDesc, pSession->headerSignature, SHA1_HASH_SIZE) ==
276                                SHA1_HASH_SIZE) {
277                    isSuccess = FwdLockFile_DeriveKeys(pSession);
278                }
279            }
280        }
281        if (isSuccess) {
282            pSession->dataOffset = pSession->contentTypeLength +
283                    pSession->encryptedSessionKeyLength + TOP_HEADER_SIZE + 2 * SHA1_HASH_SIZE;
284            pSession->filePos = 0;
285            pSession->blockIndex = INVALID_BLOCK_INDEX;
286        } else {
287            FwdLockFile_ReleaseSession(sessionId);
288            sessionId = -1;
289        }
290    }
291    return (sessionId >= 0) ? 0 : -1;
292}
293
294int FwdLockFile_open(const char *pFilename) {
295    int fileDesc = open(pFilename, O_RDONLY);
296    if (fileDesc >= 0 && FwdLockFile_attach(fileDesc) < 0) {
297        (void)close(fileDesc);
298        fileDesc = -1;
299    }
300    return fileDesc;
301}
302
303ssize_t FwdLockFile_read(int fileDesc, void *pBuffer, size_t numBytes) {
304    ssize_t numBytesRead;
305    int sessionId = FwdLockFile_FindSession(fileDesc);
306    if (sessionId < 0) {
307        numBytesRead = -1;
308    } else {
309        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
310        ssize_t i;
311        numBytesRead = read(pSession->fileDesc, pBuffer, numBytes);
312        for (i = 0; i < numBytesRead; ++i) {
313            FwdLockFile_DecryptByte(pSession, &((unsigned char *)pBuffer)[i]);
314            ++pSession->filePos;
315        }
316    }
317    return numBytesRead;
318}
319
320off64_t FwdLockFile_lseek(int fileDesc, off64_t offset, int whence) {
321    off64_t newFilePos;
322    int sessionId = FwdLockFile_FindSession(fileDesc);
323    if (sessionId < 0) {
324        newFilePos = INVALID_OFFSET;
325    } else {
326        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
327        switch (whence) {
328        case SEEK_SET:
329            newFilePos = lseek64(pSession->fileDesc, pSession->dataOffset + offset, whence);
330            break;
331        case SEEK_CUR:
332        case SEEK_END:
333            newFilePos = lseek64(pSession->fileDesc, offset, whence);
334            break;
335        default:
336            errno = EINVAL;
337            newFilePos = INVALID_OFFSET;
338            break;
339        }
340        if (newFilePos != INVALID_OFFSET) {
341            if (newFilePos < pSession->dataOffset) {
342                // The new file position is illegal for an internal Forward Lock file. Restore the
343                // original file position.
344                (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
345                              SEEK_SET);
346                errno = EINVAL;
347                newFilePos = INVALID_OFFSET;
348            } else {
349                // The return value should be the file position that lseek64() would have returned
350                // for the embedded content file.
351                pSession->filePos = newFilePos - pSession->dataOffset;
352                newFilePos = pSession->filePos;
353            }
354        }
355    }
356    return newFilePos;
357}
358
359int FwdLockFile_detach(int fileDesc) {
360    int sessionId = FwdLockFile_FindSession(fileDesc);
361    if (sessionId < 0) {
362        return -1;
363    }
364    HMAC_CTX_cleanup(&sessionPtrs[sessionId]->signingContext);
365    FwdLockFile_ReleaseSession(sessionId);
366    return 0;
367}
368
369int FwdLockFile_close(int fileDesc) {
370    return (FwdLockFile_detach(fileDesc) == 0) ? close(fileDesc) : -1;
371}
372
373int FwdLockFile_CheckDataIntegrity(int fileDesc) {
374    int result;
375    int sessionId = FwdLockFile_FindSession(fileDesc);
376    if (sessionId < 0) {
377        result = FALSE;
378    } else {
379        struct FwdLockFile_CheckDataIntegrity_Data {
380            unsigned char signature[SHA1_HASH_SIZE];
381            unsigned char buffer[SIG_CALC_BUFFER_SIZE];
382        } *pData = malloc(sizeof *pData);
383        if (pData == NULL) {
384            result = FALSE;
385        } else {
386            FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
387            if (lseek64(pSession->fileDesc, pSession->dataOffset, SEEK_SET) !=
388                    pSession->dataOffset) {
389                result = FALSE;
390            } else {
391                ssize_t numBytesRead;
392                unsigned int signatureSize = SHA1_HASH_SIZE;
393                while ((numBytesRead =
394                        read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) {
395                    HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead);
396                }
397                if (numBytesRead < 0) {
398                    result = FALSE;
399                } else {
400                    HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize);
401                    assert(signatureSize == SHA1_HASH_SIZE);
402                    result = memcmp(pData->signature, pSession->dataSignature, SHA1_HASH_SIZE) == 0;
403                }
404                HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
405                (void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
406                              SEEK_SET);
407            }
408            free(pData);
409        }
410    }
411    return result;
412}
413
414int FwdLockFile_CheckHeaderIntegrity(int fileDesc) {
415    int result;
416    int sessionId = FwdLockFile_FindSession(fileDesc);
417    if (sessionId < 0) {
418        result = FALSE;
419    } else {
420        FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
421        unsigned char signature[SHA1_HASH_SIZE];
422        unsigned int signatureSize = SHA1_HASH_SIZE;
423        HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
424        HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType,
425                    pSession->contentTypeLength);
426        HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
427                    pSession->encryptedSessionKeyLength);
428        HMAC_Update(&pSession->signingContext, pSession->dataSignature, SHA1_HASH_SIZE);
429        HMAC_Final(&pSession->signingContext, signature, &signatureSize);
430        assert(signatureSize == SHA1_HASH_SIZE);
431        result = memcmp(signature, pSession->headerSignature, SHA1_HASH_SIZE) == 0;
432        HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
433    }
434    return result;
435}
436
437int FwdLockFile_CheckIntegrity(int fileDesc) {
438    return FwdLockFile_CheckHeaderIntegrity(fileDesc) && FwdLockFile_CheckDataIntegrity(fileDesc);
439}
440
441const char *FwdLockFile_GetContentType(int fileDesc) {
442    int sessionId = FwdLockFile_FindSession(fileDesc);
443    if (sessionId < 0) {
444        return NULL;
445    }
446    return sessionPtrs[sessionId]->pContentType;
447}
448