AES с CommonCrypto использует слишком много памяти - Objective-C

Моя цель - иметь возможность, получив файл / папку и пароль, зашифровать и расшифровать их в AES, используя Цель-C. Я не крипто-ботаник или что-то в этом роде, но я выбрал AES, потому что нашел его довольно стандартным и очень безопасным. Я использую категорию NSMutableData, в которой есть методы для шифрования и дешифрования данных. Вот он:

- (NSInteger)AES256EncryptionWithKey: (NSString*)key {
    // The key should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr));     // fill with zeroes (for padding)

    // Fetch key data
    if (![key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
    { return 2; } // Length of 'key' is bigger than keyPtr

    NSUInteger dataLength = [self length];

    // See the doc: For block ciphers, the output size will always be less than or 
    // equal to the input size plus the size of one block.
    // That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void* buffer = malloc(bufferSize);

    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL ,                    // initialization vector (optional)
                                          [self bytes], dataLength, // input bytes and it's length
                                          buffer, bufferSize,       // output buffer and it's length
                                          &numBytesEncrypted);      // ??
    if (cryptStatus == kCCSuccess) {
        // The returned NSData takes ownership of the buffer and will free it on deallocation
        [self setData: [NSData dataWithBytesNoCopy: buffer length: numBytesEncrypted]];
        return 0;
    }

    free(buffer); // Free the buffer;
    return 1;
}

- (NSInteger)AES256DecryptionWithKey: (NSString*)key {
    // The key should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr));     // fill with zeroes (for padding)

    // Fetch key data
    if (![key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
    { return 2; } // Length of 'key' is bigger than keyPtr

    NSUInteger dataLength = [self length];

    // See the doc: For block ciphers, the output size will always be less than or 
    // equal to the input size plus the size of one block.
    // That's why we need to add the size of one block here
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void* buffer = malloc(bufferSize);

    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                          keyPtr, kCCKeySizeAES256,
                                          NULL, // initialization vector (optional)
                                          [self bytes], dataLength, // input
                                          buffer, bufferSize, // output
                                          &numBytesDecrypted);

    if (cryptStatus == kCCSuccess) {
        // The returned NSData takes ownership of the buffer and will free it on deallocation
        [self setData: [NSData dataWithBytesNoCopy: buffer length: numBytesDecrypted]];
        return 0;
    }

    free(buffer); // Free the buffer;
    return 1;
}

Проблема с этим кодом в том, что он использует about !! 5 !! раз в памяти размер файла (открытого с помощью NSMutableData), который выбирает пользователь. Это совершенно неприемлемо с точки зрения пользователя (представьте себе шифрование файла размером 2–10 ГБ в памяти ??), но я действительно в затруднении.

Не могли бы вы предложить какие-либо модификации, которые решат эту проблему? Вероятно, шифрование одного фрагмента за раз (таким образом, в памяти одновременно находится только один или два фрагмента, а не весь файл * 5). Большая проблема в том, что я не знаю, как это делать. Есть идеи?

Спасибо

PS: Когда я использую эту категорию, я делаю это следующим образом:

NSMutableData* data = [NSMutableData dataWithContentsOfFile: @"filepath"];
[data AES256EncryptionWithKey: @"password"];
[data writeToFile: @"newname" atomically: NO];

И только эти 3 строки создают такую ​​большую проблему с памятью.

Ой, между прочим: я могу нужен вектор инициализации? Я думаю, что это более безопасно, или что-то в этом роде, но я не знаю. Если действительно есть необходимость, не могли бы вы рассказать мне, как это сделать?

РЕДАКТИРОВАТЬ

Вот чем я сейчас занимаюсь:

NSMutableData* data = [NSMutableData dataWithContentsOfMappedFile: @"filepath"];
[data SafeAES256EncryptionWithKey: @"password"];
[data writeToFile: @"newname" atomically: NO];

И новый метод в категории:

- (void)SafeAES256EncryptionWithKey: (NSString*)key {
    // The key should be 32 bytes for AES256, will be null-padded otherwise
    char keyPtr[kCCKeySizeAES256 + 1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr));     // fill with zeroes (for padding)

    // Fetch key data
    if (![key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding])
    { return 2; } // Length of 'key' is bigger than keyPtr

    CCCryptorRef cryptor;
    CCCryptorStatus cryptStatus = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                                  keyPtr, kCCKeySizeAES256,
                                                  NULL, // IV - needed?
                                                  &cryptor);

    if (cryptStatus != kCCSuccess) {
        ; // Handle error here
    }

    NSInteger startByte;

    size_t dataOutMoved;
    size_t dataInLength = kChunkSizeBytes; // #define kChunkSizeBytes (16)
    size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataInLength, FALSE);

    const void* dataIn = malloc(dataInLength);
    void* dataOut = malloc(dataOutLength);
    for (startByte = 0; startByte <= [self length]; startByte += kChunkSizeBytes) {
        if ((startByte + kChunkSizeBytes) > [self length]) { dataInLength = [self length] - startByte; }
        else { dataInLength = kChunkSizeBytes; }

        NSRange bytesRange = NSMakeRange(startByte, (int)dataInLength);

        [self getBytes: dataIn range: bytesRange];
        CCCryptorUpdate(cryptor, dataIn, dataInLength, dataOut, dataOutLength, &dataOutMoved);

        if (dataOutMoved != dataOutLength) {
            NSLog(@"dataOutMoved != dataOutLength");
        }

        [self replaceBytesInRange: bytesRange withBytes: dataOut];

    }

    CCCryptorFinal(cryptor, dataOut, dataOutLength, &dataOutMoved);
    [self appendBytes: dataOut length: dataOutMoved];

    CCCryptorRelease(cryptor);

Я не могу понять почему это иногда работает, а иногда нет. Я действительно в растерянности. Не мог бы кто-нибудь проверить этот код?

Чтобы не загружать весь файл в память сразу, я использую -dataWithContentsOfMappedFile , а затем вызываю -getBytes: range: , потому что Я видел здесь , что таким образом он не будет загружать сразу весь файл в реальную память, только указанный диапазон.

РЕДАКТИРОВАТЬ 2

Пожалуйста, посмотрите мой ответ о том, что я делаю сейчас.

8
задан Community 23 May 2017 в 10:32
поделиться