#include #include #include #include #include #ifdef HAVE_CRYPT #include #include #include #define CHUNKNO(oft, info) ((uint32_t)((oft)/info->blockSize)) #define CHUNKOFFSET(oft, info) ((size_t)((oft) - ((off_t)(CHUNKNO(oft, info)) * (off_t)info->blockSize))) static void flipFileVaultV2Header(FileVaultV2Header* header) { FLIPENDIAN(header->signature); FLIPENDIAN(header->version); FLIPENDIAN(header->encIVSize); FLIPENDIAN(header->unk1); FLIPENDIAN(header->unk2); FLIPENDIAN(header->unk3); FLIPENDIAN(header->unk4); FLIPENDIAN(header->unk5); FLIPENDIAN(header->unk5); FLIPENDIAN(header->blockSize); FLIPENDIAN(header->dataSize); FLIPENDIAN(header->dataOffset); FLIPENDIAN(header->kdfAlgorithm); FLIPENDIAN(header->kdfPRNGAlgorithm); FLIPENDIAN(header->kdfIterationCount); FLIPENDIAN(header->kdfSaltLen); FLIPENDIAN(header->blobEncIVSize); FLIPENDIAN(header->blobEncKeyBits); FLIPENDIAN(header->blobEncAlgorithm); FLIPENDIAN(header->blobEncPadding); FLIPENDIAN(header->blobEncMode); FLIPENDIAN(header->encryptedKeyblobSize); } static void writeChunk(FileVaultInfo* info) { unsigned char buffer[info->blockSize]; unsigned char buffer2[info->blockSize]; unsigned char msgDigest[FILEVAULT_MSGDGST_LENGTH]; uint32_t msgDigestLen; uint32_t myChunk; myChunk = info->curChunk; FLIPENDIAN(myChunk); HMAC_Init_ex(&(info->hmacCTX), NULL, 0, NULL, NULL); HMAC_Update(&(info->hmacCTX), (unsigned char *) &myChunk, sizeof(uint32_t)); HMAC_Final(&(info->hmacCTX), msgDigest, &msgDigestLen); AES_cbc_encrypt(info->chunk, buffer, info->blockSize, &(info->aesEncKey), msgDigest, AES_ENCRYPT); info->file->seek(info->file, (info->curChunk * info->blockSize) + info->dataOffset); info->file->read(info->file, buffer2, info->blockSize); info->file->seek(info->file, (info->curChunk * info->blockSize) + info->dataOffset); info->file->write(info->file, buffer, info->blockSize); info->dirty = FALSE; } static void cacheChunk(FileVaultInfo* info, uint32_t chunk) { unsigned char buffer[info->blockSize]; unsigned char msgDigest[FILEVAULT_MSGDGST_LENGTH]; uint32_t msgDigestLen; if(chunk == info->curChunk) { return; } if(info->dirty) { writeChunk(info); } info->file->seek(info->file, chunk * info->blockSize + info->dataOffset); info->file->read(info->file, buffer, info->blockSize); info->curChunk = chunk; FLIPENDIAN(chunk); HMAC_Init_ex(&(info->hmacCTX), NULL, 0, NULL, NULL); HMAC_Update(&(info->hmacCTX), (unsigned char *) &chunk, sizeof(uint32_t)); HMAC_Final(&(info->hmacCTX), msgDigest, &msgDigestLen); AES_cbc_encrypt(buffer, info->chunk, info->blockSize, &(info->aesKey), msgDigest, AES_DECRYPT); } size_t fvRead(AbstractFile* file, void* data, size_t len) { FileVaultInfo* info; size_t toRead; info = (FileVaultInfo*) (file->data); if((CHUNKOFFSET(info->offset, info) + len) > info->blockSize) { toRead = info->blockSize - CHUNKOFFSET(info->offset, info); memcpy(data, (void *)((uint8_t*)(&(info->chunk)) + CHUNKOFFSET(info->offset, info)), toRead); info->offset += toRead; cacheChunk(info, CHUNKNO(info->offset, info)); return toRead + fvRead(file, (void *)((uint8_t*)data + toRead), len - toRead); } else { toRead = len; memcpy(data, (void *)((uint8_t*)(&(info->chunk)) + CHUNKOFFSET(info->offset, info)), toRead); info->offset += toRead; cacheChunk(info, CHUNKNO(info->offset, info)); return toRead; } } size_t fvWrite(AbstractFile* file, const void* data, size_t len) { FileVaultInfo* info; size_t toRead; int i; info = (FileVaultInfo*) (file->data); if(info->dataSize < (info->offset + len)) { if(info->version == 2) { info->header.v2.dataSize = info->offset + len; } info->headerDirty = TRUE; } if((CHUNKOFFSET(info->offset, info) + len) > info->blockSize) { toRead = info->blockSize - CHUNKOFFSET(info->offset, info); for(i = 0; i < toRead; i++) { ASSERT(*((char*)((uint8_t*)(&(info->chunk)) + (uint32_t)CHUNKOFFSET(info->offset, info) + i)) == ((char*)data)[i], "blah"); } memcpy((void *)((uint8_t*)(&(info->chunk)) + (uint32_t)CHUNKOFFSET(info->offset, info)), data, toRead); info->dirty = TRUE; info->offset += toRead; cacheChunk(info, CHUNKNO(info->offset, info)); return toRead + fvWrite(file, (void *)((uint8_t*)data + toRead), len - toRead); } else { toRead = len; for(i = 0; i < toRead; i++) { ASSERT(*((char*)((uint8_t*)(&(info->chunk)) + CHUNKOFFSET(info->offset, info) + i)) == ((char*)data)[i], "blah"); } memcpy((void *)((uint8_t*)(&(info->chunk)) + CHUNKOFFSET(info->offset, info)), data, toRead); info->dirty = TRUE; info->offset += toRead; cacheChunk(info, CHUNKNO(info->offset, info)); return toRead; } } int fvSeek(AbstractFile* file, off_t offset) { FileVaultInfo* info = (FileVaultInfo*) (file->data); info->offset = offset; cacheChunk(info, CHUNKNO(offset, info)); return 0; } off_t fvTell(AbstractFile* file) { FileVaultInfo* info = (FileVaultInfo*) (file->data); return info->offset; } off_t fvGetLength(AbstractFile* file) { FileVaultInfo* info = (FileVaultInfo*) (file->data); return info->dataSize; } void fvClose(AbstractFile* file) { FileVaultInfo* info = (FileVaultInfo*) (file->data); /* force a flush */ if(info->curChunk == 0) { cacheChunk(info, 1); } else { cacheChunk(info, 0); } HMAC_CTX_cleanup(&(info->hmacCTX)); if(info->headerDirty) { if(info->version == 2) { file->seek(file, 0); flipFileVaultV2Header(&(info->header.v2)); file->write(file, &(info->header.v2), sizeof(FileVaultV2Header)); } } info->file->close(info->file); free(info); free(file); } AbstractFile* createAbstractFileFromFileVault(AbstractFile* file, const char* key) { FileVaultInfo* info; AbstractFile* toReturn; uint64_t signature; uint8_t aesKey[16]; uint8_t hmacKey[20]; int i; if(file == NULL) return NULL; file->seek(file, 0); file->read(file, &signature, sizeof(uint64_t)); FLIPENDIAN(signature); if(signature != FILEVAULT_V2_SIGNATURE) { printf("Unknown signature: %x\n", signature); /* no FileVault v1 handling yet */ return NULL; } toReturn = (AbstractFile*) malloc(sizeof(AbstractFile)); info = (FileVaultInfo*) malloc(sizeof(FileVaultInfo)); info->version = 2; file->seek(file, 0); file->read(file, &(info->header.v2), sizeof(FileVaultV2Header)); flipFileVaultV2Header(&(info->header.v2)); for(i = 0; i < 16; i++) { sscanf(&(key[i * 2]), "%02hhx", &(aesKey[i])); } for(i = 0; i < 20; i++) { sscanf(&(key[(16 * 2) + i * 2]), "%02hhx", &(hmacKey[i])); } HMAC_CTX_init(&(info->hmacCTX)); HMAC_Init_ex(&(info->hmacCTX), hmacKey, sizeof(hmacKey), EVP_sha1(), NULL); AES_set_decrypt_key(aesKey, FILEVAULT_CIPHER_KEY_LENGTH * 8, &(info->aesKey)); AES_set_encrypt_key(aesKey, FILEVAULT_CIPHER_KEY_LENGTH * 8, &(info->aesEncKey)); info->dataOffset = info->header.v2.dataOffset; info->dataSize = info->header.v2.dataSize; info->blockSize = info->header.v2.blockSize; info->offset = 0; info->file = file; info->headerDirty = FALSE; info->dirty = FALSE; info->curChunk = 1; /* just to set it to a value not 0 */ cacheChunk(info, 0); toReturn->data = info; toReturn->read = fvRead; toReturn->write = fvWrite; toReturn->seek = fvSeek; toReturn->tell = fvTell; toReturn->getLength = fvGetLength; toReturn->close = fvClose; return toReturn; } #else AbstractFile* createAbstractFileFromFileVault(AbstractFile* file, const char* key) { return NULL; } #endif