From 67cbe2f1bd82ddad55e22df5282070cb7e36c9ae Mon Sep 17 00:00:00 2001 From: guoling Date: Fri, 5 Jul 2019 16:08:50 +0800 Subject: [PATCH] store randowm iv inside meta file --- Script/formatCode.py | 2 +- Win32/MMKV/aes/AESCrypt.h | 2 +- iOS/MMKV/MMKV/MMKV.mm | 112 +++++++++++++++++++++------------ iOS/MMKV/MMKV/MMKVMetaInfo.hpp | 2 + iOS/MMKV/MMKV/aes/AESCrypt.h | 9 ++- iOS/MMKV/MMKV/aes/AESCrypt.mm | 34 ++++++++-- 6 files changed, 111 insertions(+), 50 deletions(-) diff --git a/Script/formatCode.py b/Script/formatCode.py index e34336de..7a635a9b 100644 --- a/Script/formatCode.py +++ b/Script/formatCode.py @@ -6,7 +6,7 @@ import os.path path = os.getcwd() for root, dirs, files in os.walk(path): - if root.endswith("/aes") or root.endswith("/openssl") or root.endswith("/zlib") or (root.find("/build/generated/") != -1): + if root.endswith("/openssl") or root.endswith("/zlib") or (root.find("/build/generated/") != -1): continue for name in files: if (name.endswith(".h") or name.endswith(".m") or name.endswith(".mm") or name.endswith(".hpp") or name.endswith(".cpp") or name.endswith(".java")): diff --git a/Win32/MMKV/aes/AESCrypt.h b/Win32/MMKV/aes/AESCrypt.h index 21c4ea6e..7c7af9aa 100644 --- a/Win32/MMKV/aes/AESCrypt.h +++ b/Win32/MMKV/aes/AESCrypt.h @@ -29,7 +29,7 @@ constexpr size_t AES_KEY_BITSET_LEN = 128; namespace mmkv { - // a AES CFB-128 encrypt-decrypt full-duplex wrapper +// a AES CFB-128 encrypt-decrypt full-duplex wrapper class AESCrypt { unsigned char m_vector[AES_KEY_LEN] = {0}; unsigned char m_key[AES_KEY_LEN] = {0}; diff --git a/iOS/MMKV/MMKV/MMKV.mm b/iOS/MMKV/MMKV/MMKV.mm index f2ca48d7..3303d377 100644 --- a/iOS/MMKV/MMKV/MMKV.mm +++ b/iOS/MMKV/MMKV/MMKV.mm @@ -78,8 +78,8 @@ enum : bool { uint32_t m_crcDigest; MMKVMetaInfo m_metaInfo; - int m_crcFd; - char *m_crcPtr; + int m_metaFd; + char *m_metaFilePtr; } #pragma mark - init @@ -191,13 +191,13 @@ enum : bool { m_cryptor = nullptr; } - if (m_crcPtr != nullptr && m_crcPtr != MAP_FAILED) { - munmap(m_crcPtr, CRC_FILE_SIZE); - m_crcPtr = nullptr; + if (m_metaFilePtr != nullptr && m_metaFilePtr != MAP_FAILED) { + munmap(m_metaFilePtr, CRC_FILE_SIZE); + m_metaFilePtr = nullptr; } - if (m_crcFd >= 0) { - close(m_crcFd); - m_crcFd = -1; + if (m_metaFd >= 0) { + close(m_metaFd); + m_metaFd = -1; } } @@ -241,9 +241,14 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { } - (void)loadFromFile { - [self prepareCRCFile]; - if (m_crcPtr != nullptr && m_crcPtr != MAP_FAILED) { - m_metaInfo.read(m_crcPtr); + [self prepareMetaFile]; + if (m_metaFilePtr != nullptr && m_metaFilePtr != MAP_FAILED) { + m_metaInfo.read(m_metaFilePtr); + } + if (m_cryptor) { + if (m_metaInfo.m_version >= 2) { + m_cryptor->reset(m_metaInfo.m_vector, sizeof(m_metaInfo.m_vector)); + } } m_fd = open(m_path.UTF8String, O_RDWR, S_IRWXU); @@ -400,8 +405,10 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { m_actualSize = 0; m_crcDigest = 0; + m_metaInfo.m_crcDigest = 0; + [self updateIVAndIncreaseSequence:IncreaseSequence]; if (m_cryptor) { - m_cryptor->reset(); + m_cryptor->reset(m_metaInfo.m_vector, sizeof(m_metaInfo.m_vector)); } [self loadFromFile]; @@ -439,9 +446,14 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { m_fd = -1; m_size = 0; m_actualSize = 0; + m_metaInfo.m_crcDigest = 0; if (m_cryptor) { - m_cryptor->reset(); + if (m_metaInfo.m_version >= 2) { + m_cryptor->reset(m_metaInfo.m_vector, sizeof(m_metaInfo.m_vector)); + } else { + m_cryptor->reset(); + } } } @@ -593,7 +605,8 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { } if (m_cryptor) { - m_cryptor->reset(); + [self updateIVAndIncreaseSequence:KeepSequence]; + m_cryptor->reset(m_metaInfo.m_vector, sizeof(m_metaInfo.m_vector)); auto ptr = (unsigned char *) data.bytes; m_cryptor->encrypt(ptr, ptr, data.length); } @@ -721,7 +734,8 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { int offset = pbFixed32Size(0); if (allData.length + offset <= m_size) { if (m_cryptor) { - m_cryptor->reset(); + [self updateIVAndIncreaseSequence:KeepSequence]; + m_cryptor->reset(m_metaInfo.m_vector, sizeof(m_metaInfo.m_vector)); auto ptr = (unsigned char *) allData.bytes; m_cryptor->encrypt(ptr, ptr, allData.length); } @@ -761,14 +775,14 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { int offset = pbFixed32Size(0); m_crcDigest = (uint32_t) crc32(0, (const uint8_t *) m_ptr + offset, (uint32_t) m_actualSize); - if (m_crcPtr == nullptr || m_crcPtr == MAP_FAILED) { - [self prepareCRCFile]; + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { + [self prepareMetaFile]; } - if (m_crcPtr == nullptr || m_crcPtr == MAP_FAILED) { + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { MMKVError(@"Meta file not valid %@", m_mmapID); return NO; } - m_metaInfo.read(m_crcPtr); + m_metaInfo.read(m_metaFilePtr); if (m_crcDigest == m_metaInfo.m_crcDigest) { return YES; } @@ -791,18 +805,18 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { } m_crcDigest = (uint32_t) crc32(m_crcDigest, ptr, (uint32_t) length); - if (m_crcPtr == nullptr || m_crcPtr == MAP_FAILED) { - [self prepareCRCFile]; + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { + [self prepareMetaFile]; } - if (m_crcPtr == nullptr || m_crcPtr == MAP_FAILED) { + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { return; } static const size_t bufferLength = pbFixed32Size(0); auto hasMlock = false; if (g_isInBackground) { - if (mlock(m_crcPtr, bufferLength) != 0) { - MMKVError(@"fail to mlock crc [%@]-%p, %d:%s", m_mmapID, m_crcPtr, errno, strerror(errno)); + if (mlock(m_metaFilePtr, bufferLength) != 0) { + MMKVError(@"fail to mlock crc [%@]-%p, %d:%s", m_mmapID, m_metaFilePtr, errno, strerror(errno)); // just fail on this condition, otherwise app will crash anyway return; } @@ -816,49 +830,67 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { if (m_metaInfo.m_version == 0) { m_metaInfo.m_version = 1; } - m_metaInfo.write(m_crcPtr); + m_metaInfo.write(m_metaFilePtr); if (hasMlock) { - munlock(m_crcPtr, bufferLength); + munlock(m_metaFilePtr, bufferLength); } } -- (void)prepareCRCFile { - if (m_crcPtr == nullptr || m_crcPtr == MAP_FAILED) { +- (void)prepareMetaFile { + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { if (!isFileExist(m_crcPath)) { createFile(m_crcPath); } - m_crcFd = open(m_crcPath.UTF8String, O_RDWR, S_IRWXU); - if (m_crcFd < 0) { + m_metaFd = open(m_crcPath.UTF8String, O_RDWR, S_IRWXU); + if (m_metaFd < 0) { MMKVError(@"fail to open:%@, %s", m_crcPath, strerror(errno)); removeFile(m_crcPath); } else { size_t size = 0; struct stat st = {}; - if (fstat(m_crcFd, &st) != -1) { + if (fstat(m_metaFd, &st) != -1) { size = (size_t) st.st_size; } int fileLegth = CRC_FILE_SIZE; if (size != fileLegth) { size = fileLegth; - if (ftruncate(m_crcFd, size) != 0) { + if (ftruncate(m_metaFd, size) != 0) { MMKVError(@"fail to truncate [%@] to size %zu, %s", m_crcPath, size, strerror(errno)); - close(m_crcFd); - m_crcFd = -1; + close(m_metaFd); + m_metaFd = -1; removeFile(m_crcPath); return; } } - m_crcPtr = (char *) mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_crcFd, 0); - if (m_crcPtr == MAP_FAILED) { + m_metaFilePtr = (char *) mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_metaFd, 0); + if (m_metaFilePtr == MAP_FAILED) { MMKVError(@"fail to mmap [%@], %s", m_crcPath, strerror(errno)); - close(m_crcFd); - m_crcFd = -1; + close(m_metaFd); + m_metaFd = -1; } } } } +- (void)updateIVAndIncreaseSequence:(bool)increaseSequence { + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { + [self prepareMetaFile]; + } + if (m_metaFilePtr == nullptr || m_metaFilePtr == MAP_FAILED) { + return; + } + + if (increaseSequence) { + m_metaInfo.m_sequence++; + } + if (m_metaInfo.m_version < 2) { + m_metaInfo.m_version = 2; + } + AESCrypt::fillRandomIV(m_metaInfo.m_vector); + m_metaInfo.write(m_metaFilePtr); +} + #pragma mark - encryption & decryption - (nullable NSData *)cryptKey { @@ -1318,7 +1350,7 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { - (void)doSync:(bool)sync { CScopedLock lock(m_lock); - if (m_needLoadFromFile || ![self isFileValid] || m_crcPtr == nullptr) { + if (m_needLoadFromFile || ![self isFileValid] || m_metaFilePtr == nullptr) { return; } @@ -1326,7 +1358,7 @@ NSData *decryptBuffer(AESCrypt &crypter, NSData *inputBuffer) { if (msync(m_ptr, m_actualSize, flag) != 0) { MMKVError(@"fail to msync[%d] data file of [%@]:%s", flag, m_mmapID, strerror(errno)); } - if (msync(m_crcPtr, CRC_FILE_SIZE, flag) != 0) { + if (msync(m_metaFilePtr, CRC_FILE_SIZE, flag) != 0) { MMKVError(@"fail to msync[%d] crc-32 file of [%@]:%s", flag, m_mmapID, strerror(errno)); } } diff --git a/iOS/MMKV/MMKV/MMKVMetaInfo.hpp b/iOS/MMKV/MMKV/MMKVMetaInfo.hpp index 157bf832..7e8f2780 100644 --- a/iOS/MMKV/MMKV/MMKVMetaInfo.hpp +++ b/iOS/MMKV/MMKV/MMKVMetaInfo.hpp @@ -21,6 +21,7 @@ #ifndef MMKV_MMKVMETAINFO_H #define MMKV_MMKVMETAINFO_H +#include "AESCrypt.h" #include #include #include @@ -29,6 +30,7 @@ struct MMKVMetaInfo { uint32_t m_crcDigest = 0; uint32_t m_version = 1; uint32_t m_sequence = 0; // full write-back count + unsigned char m_vector[AES_KEY_LEN] = {0}; void write(void *ptr) { assert(ptr); diff --git a/iOS/MMKV/MMKV/aes/AESCrypt.h b/iOS/MMKV/MMKV/aes/AESCrypt.h index ac10a221..86376258 100644 --- a/iOS/MMKV/MMKV/aes/AESCrypt.h +++ b/iOS/MMKV/MMKV/aes/AESCrypt.h @@ -35,16 +35,21 @@ class AESCrypt { int m_number = 0; public: - AESCrypt(const unsigned char *key, size_t keyLength); + AESCrypt(const unsigned char *key, + size_t keyLength, + const unsigned char *iv = nullptr, + size_t ivLength = 0); void encrypt(const unsigned char *input, unsigned char *output, size_t length); void decrypt(const unsigned char *input, unsigned char *output, size_t length); - void reset(); + void reset(const unsigned char *iv = nullptr, size_t ivLength = 0); // output must have [AES_KEY_LEN] space void getKey(void *output) const; + + static void fillRandomIV(unsigned char *vector); }; #ifndef NDEBUG diff --git a/iOS/MMKV/MMKV/aes/AESCrypt.mm b/iOS/MMKV/MMKV/aes/AESCrypt.mm index 4d62140a..695deebc 100644 --- a/iOS/MMKV/MMKV/aes/AESCrypt.mm +++ b/iOS/MMKV/MMKV/aes/AESCrypt.mm @@ -24,19 +24,24 @@ #include #include -AESCrypt::AESCrypt(const unsigned char *key, size_t keyLength) { +AESCrypt::AESCrypt(const unsigned char *key, size_t keyLength, const unsigned char *iv, size_t ivLength) { if (key && keyLength > 0) { memcpy(m_key, key, (keyLength > AES_KEY_LEN) ? AES_KEY_LEN : keyLength); - memcpy(m_vector, m_key, AES_KEY_LEN); + + reset(iv, ivLength); int ret = openssl::AES_set_encrypt_key(m_key, AES_KEY_BITSET_LEN, &m_aesKey); assert(ret == 0); } } -void AESCrypt::reset() { +void AESCrypt::reset(const unsigned char *iv, size_t ivLength) { m_number = 0; - memcpy(m_vector, m_key, AES_KEY_LEN); + if (iv && ivLength > 0) { + memcpy(m_vector, iv, (ivLength > AES_KEY_LEN) ? AES_KEY_LEN : ivLength); + } else { + memcpy(m_vector, m_key, AES_KEY_LEN); + } } void AESCrypt::getKey(void *output) const { @@ -59,6 +64,17 @@ void AESCrypt::decrypt(const unsigned char *input, unsigned char *output, size_t openssl::AES_cfb128_encrypt(input, output, length, &m_aesKey, m_vector, &m_number, AES_DECRYPT); } +void AESCrypt::fillRandomIV(unsigned char *vector) { + if (!vector) { + return; + } + srand((unsigned) time(NULL)); + int *ptr = (int *) vector; + for (int i = 0; i < AES_KEY_LEN / sizeof(int); i++) { + ptr[i] = rand(); + } +} + #ifndef NDEBUG #include "MemoryFile.h" @@ -70,8 +86,14 @@ void testAESCrypt() { const unsigned char key[] = "TheAESKey"; constexpr size_t keyLength = sizeof(key) - 1; - AESCrypt crypt1(key, keyLength); - AESCrypt crypt2(key, keyLength); + + unsigned char iv[AES_KEY_LEN]; + srand((unsigned) time(NULL)); + for (int i = 0; i < AES_KEY_LEN; i++) { + iv[i] = rand(); + } + AESCrypt crypt1(key, keyLength, iv, sizeof(iv)); + AESCrypt crypt2(key, keyLength, iv, sizeof(iv)); auto encryptText = new unsigned char[DEFAULT_MMAP_SIZE]; auto decryptText = new unsigned char[DEFAULT_MMAP_SIZE];