2011-05-25 2 views
2

J'ai un fichier crypté avec AES. J'utilise la catégorie NSData suivante:NSData avec CCCrypt dans un environnement multithread

#import <CommonCrypto/CommonCryptor.h> 

@implementation NSData (AES) 

- (NSData *)AES256DecryptWithKey:(NSString *)key { 

    // '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 
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]; 

    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); 

    NSLog(@"Bytes decrypted: %d",numBytesDecrypted); 

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

    NSLog(@"Decrypt failed with error code %d",cryptStatus); 
    free(buffer); //free the buffer; 
    return nil; 
} 

@end 

Le processus de descrypt semble fonctionner correctement lorsque je charge le fichier entier du système de fichiers avec le code suivant:

[NSData dataWithContentsOfFile:dataPath]; 

Le problème se produit lorsque le fichier est pas lire avec l'appel précédent, mais quand un code externe qui tronque le fichier et init un NSData avec seulement un petit morceau de données et essayer de déchiffrer cela, en particulier les problèmes se produit lorsque différents threads utilisent ce code (ou du moins est ce que je pense):

- (NSData *)readDataOfLength:(NSUInteger)length 
{ 
    HTTPLogTrace2(@"%@[%p]: readDataOfLength:%lu", THIS_FILE, self, (unsigned long)length); 

    if (![self openFileIfNeeded]) 
    { 
     // File opening failed, 
     // or response has been aborted due to another error. 
     return nil; 
    } 

    // Determine how much data we should read. 
    // 
    // It is OK if we ask to read more bytes than exist in the file. 
    // It is NOT OK to over-allocate the buffer. 

    UInt64 bytesLeftInFile = fileLength - fileOffset; 

    NSUInteger bytesToRead = (NSUInteger)MIN(length, bytesLeftInFile); 

    // Make sure buffer is big enough for read request. 
    // Do not over-allocate. 

    if (buffer == NULL || bufferSize < bytesToRead) 
    { 
     bufferSize = bytesToRead; 
     buffer = reallocf(buffer, (size_t)bufferSize); 

     if (buffer == NULL) 
     { 
      HTTPLogError(@"%@[%p]: Unable to allocate buffer", THIS_FILE, self); 

      [self abort]; 
      return nil; 
     } 
    } 

    // Perform the read 

    HTTPLogVerbose(@"%@[%p]: Attempting to read %lu bytes from file", THIS_FILE, self, bytesToRead); 

    ssize_t result = read(fileFD, buffer, bytesToRead); 

    // Check the results 

    if (result < 0) 
    { 
     HTTPLogError(@"%@: Error(%i) reading file(%@)", THIS_FILE, errno, filePath); 

     [self abort]; 
     return nil; 
    } 
    else if (result == 0) 
    { 
     HTTPLogError(@"%@: Read EOF on file(%@)", THIS_FILE, filePath); 

     [self abort]; 
     return nil; 
    } 
    else // (result > 0) 
    { 
     HTTPLogVerbose(@"%@[%p]: Read %d bytes from file", THIS_FILE, self, result); 

     fileOffset += result; 

     NSData *data = [NSData dataWithBytes:buffer length:result]; 
     return [data AES256DecryptWithKey:@"abcdefghijklmnopqrstuvwxyz123456"]; 
     //return data; 
    } 
} 

Qu'est-ce qui se passe est que la fonction CCCrypt dans ce cas échouer avec le code d'erreur -4304 AKA "kCCDecodeError - Données d'entrée n'a pas décodé ou déchiffrer correctement." De plus, si dans l'appel CCCrypt au lieu de kCCOptionPKCS7Padding je passe 0 -> pas de remplissage la méthode déchiffre le premier morceau de données mais quand le thread est commuté échoue avec -4300 AKA "kCCParamError - valeur de paramètre illégal."

Avec le message suivant dans la console:

[Switching to process 13059 thread 0x0] 
2011-05-25 18:00:03.631 Drm[1843:6e0b] Bytes decrypted: 131072 
2011-05-25 18:00:03.647 Drm[1843:6e0b] Bytes decrypted: 68096 
[Switching to process 11779 thread 0x0] 
2011-05-25 18:00:04.547 Drm[1843:6e0b] Bytes decrypted: 0 
2011-05-25 18:00:04.555 Drm[1843:6e0b] Decrypt failed with error code -4300 

Quelqu'un peut-il aider?

Répondre

2

AES est un chiffrement par bloc. Vous devez le déchiffrer un bloc à la fois. Un bloc AES est 128 bits (ceci n'est pas lié au "256" dans AES256DecryptWithKey). Vous devez donc vous assurer que les données que vous transmettez correspondent à un multiple de 16 octets.

Je n'ai pas essayé d'utiliser CCCrypt() de cette façon, ce n'est pas vraiment ce que c'est. CCCrypt() est une fonction pratique lorsque vous voulez effectuer un décryptage en une fois. Lorsque vous voulez faire "comme vous allez" décryptage, vous utilisez CCCryptorCreate(), puis plusieurs appels à CCCryptorUpdate() et enfin CCCryptorFinal() (ou vous pouvez appeler CCCryptorFinal(), puis CCCryptorReset() pour décrypter plus de choses avec la même clé). Enfin, vous appelez CCCryptorRelease() pour libérer votre cryptor.

EDIT Je pensais à ce sujet un peu plus, et se rendit compte que CCCrypt() ne peut pas être utilisé de cette façon, même si vous avez rompu l'entrée en morceaux de 16 octets. Chaque bloc de chiffrement AES modifie l'IV du bloc suivant, de sorte que vous ne pouvez pas démarrer quelqu'un au milieu d'un flux. C'est pourquoi vous avez besoin d'un objet CCCryptor persistant pendant toute la session.

+0

Donc, votre suggestion est d'avoir une référence partagée de CCCryptor et l'utiliser entre plusieurs appels CCCryptorUpdate()? Je vais certainement essayer et je vous le ferai savoir! Mais l'option kCCOptionPKCS7Padding n'est pas utilisée pour ne pas se préoccuper du rembourrage? –

+0

kCCOptionPKCS7Padding indique qu'il doit appliquer un remplissage PKCS à ce que vous lui remettez. Vous le transmettez au milieu d'un cours d'eau. Vous ne pouvez pas appliquer un remplissage aléatoire au milieu d'un flux. (Vous devez également garder une trace de votre IV, donc vous ne pouvez pas non plus commencer à déchiffrer au milieu du flux.) –

+0

J'ai essayé d'implémenter ce que vous m'avez suggéré, mais il y a encore des problèmes: la classe qui appelle mon code et nécessite donc le morceau de données (et dont je n'ai aucun contrôle) nécessite parfois le morceau de données qui ne sont pas multiples de 16 octets plutôt généralement requis est de 2 octets et cela brise mon processus de décryptage –

Questions connexes