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