2014-09-03 5 views
1

Я написал код, который использует Common Crypto для iOS для шифрования и дешифрования объекта NSData. Ключами шифрования являются AES128 и хранятся в цепочке ключей iOS. Я могу зашифровать и дешифровать данные успешно, поэтому я знаю, что часть кода работает. Тем не менее, как проверка работоспособности, я также создал второй ключ AES128 и попытался расшифровать данные, которые были зашифрованы с помощью ключа шифрования сперва. Я ожидал, что значение CCCryptorStatus будет чем-то иным, чем kCCSuccess, однако это было не так. Я получил обратно объект NSData и никаких ошибок. Мой зашифровать/расшифровать код выглядит примерно так ...Common Crypto - тестирование плохого ключа в расшифровке?

-(NSData *)dataDecryptedUsingAlgorithm:(CCAlgorithm)algorithm 
            data:(NSData *)data 
            key:(id)key 
        initializationVector:(id)iv 
           options:(CCOptions)options 
           error:(CCCryptorStatus *)error { 
    CCCryptorRef cryptor = NULL; 
    CCCryptorStatus status = kCCSuccess; 

    NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]); 
    NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]); 

    NSMutableData * keyData, * ivData; 
    if ([key isKindOfClass: [NSData class]]) 
     keyData = (NSMutableData *) [key mutableCopy]; 
    else 
     keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy]; 

    if ([iv isKindOfClass: [NSString class]]) 
     ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy]; 
    else 
     ivData = (NSMutableData *) [iv mutableCopy]; // data or nil 

    // [keyData autorelease]; 
    // [ivData autorelease]; 

    // ensure correct lengths for key and iv data, based on algorithms 
    FixKeyLengths(algorithm, keyData, ivData); 

    status = CCCryptorCreate(kCCDecrypt, algorithm, options, 
          [keyData bytes], [keyData length], [ivData bytes], 
          &cryptor); 

    if (status != kCCSuccess) 
    { 
     if (error != NULL) 
      *error = status; 
     return (nil); 
    } 

    NSData *result = [self runCryptor:cryptor onData:data result:&status]; 
    if ((result == nil) && (error != NULL)) 
     *error = status; 

    CCCryptorRelease(cryptor); 

    return (result); 
} 

-(NSData *)dataEncryptedUsingAlgorithm:(CCAlgorithm) algorithm 
            data:(NSData *)data 
            key:(id)key 
        initializationVector:(id)iv 
           options:(CCOptions)options 
           error:(CCCryptorStatus *)error { 
    CCCryptorRef cryptor = NULL; 
    CCCryptorStatus status = kCCSuccess; 

    NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]); 
    NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]); 

    NSMutableData * keyData, * ivData; 
    if ([key isKindOfClass: [NSData class]]) 
     keyData = (NSMutableData *) [key mutableCopy]; 
    else 
     keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy]; 

    if ([iv isKindOfClass: [NSString class]]) 
     ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy]; 
    else 
     ivData = (NSMutableData *) [iv mutableCopy]; // data or nil 

    // [keyData autorelease]; 
    // [ivData autorelease]; 

    // ensure correct lengths for key and iv data, based on algorithms 
    FixKeyLengths(algorithm, keyData, ivData); 

    status = CCCryptorCreate(kCCEncrypt, algorithm, options, 
          [keyData bytes], [keyData length], [ivData bytes], 
          &cryptor); 

    if (status != kCCSuccess) 
    { 
     if (error != NULL) 
      *error = status; 
     return (nil); 
    } 

    NSData *result = [self runCryptor:cryptor onData:data result:&status]; 
    if ((result == nil) && (error != NULL)) 
     *error = status; 

    CCCryptorRelease(cryptor); 

    return (result); 
} 

-(NSData *)runCryptor:(CCCryptorRef)cryptor onData:(NSData *)data result:(CCCryptorStatus *)status { 
    size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[data length], true); 
    void * buf = malloc(bufsize); 
    size_t bufused = 0; 
    size_t bytesTotal = 0; 
    *status = CCCryptorUpdate(cryptor, [data bytes], (size_t)[data length], 
           buf, bufsize, &bufused); 
    if (*status != kCCSuccess) 
    { 
     free(buf); 
     return (nil); 
    } 

    bytesTotal += bufused; 

    // From Brent Royal-Gordon (Twitter: architechies): 
    // Need to update buf ptr past used bytes when calling CCCryptorFinal() 
    *status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused); 
    if (*status != kCCSuccess) 
    { 
     free(buf); 
     return (nil); 
    } 

    bytesTotal += bufused; 

    return ([NSData dataWithBytesNoCopy: buf length: bytesTotal]); 
} 

Когда я называю шифровать и расшифровывать методы, я передаю в kCCAlgorithmAES128 как мой алгоритм, и kCCOptionPKCS7Padding как мои варианты. Есть ли способ поймать, когда для дешифрования используется плохая клавиша, поэтому я могу вернуть соответствующую ошибку?

ответ

2

Единственный надежный способ отличить плохой ключ от поврежденных данных - это, как отмечает Zaph, какая-то кроватка (т. Е. Используется в самом широком смысле этого слова, т. Е. Что-то, что вы знаете о шифровании). Если вы заинтересованы в подходе к этому, см. Раздел RNCryptor v4 spec. Это еще не реализация, это просто спецификация, но она включает в себя поле валидатора, которое может использоваться для определения правильности пароля. Он использует шаг HKDF-Expand, который преобразует часть вашего исходного материала для ввода в токен проверки.


Как примечание, эта часть вашего метода весьма относительно:

if ([iv isKindOfClass: [NSString class]]) 
    ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy]; 
else 
    ivData = (NSMutableData *) [iv mutableCopy]; // data or nil 

Если строка передается в это имеет значительно меньшую, чем пространство ключей вы можете ожидать. Даже если это 16 абсолютно случайных байтов строки, законные строки UTF8 представляют собой гораздо меньшее пространство, чем эквивалентные 16 байт случайных данных.

+0

Лично мне нравится KCV (значения проверки ключа), поскольку они дают вам некоторое представление о том, что может быть неправильным. Но KCV также может стать поврежденным, поэтому вы по-прежнему не можете отличить плохой ключ от поврежденных данных. См. Также мой вопрос [здесь] (http://crypto.stackexchange.com/q/1930/1172). Обратите внимание, что ваш протокол использует KCV и MAC - просто добавление KCV недостаточно, вы должны сделать это ясно в своем ответе. –

+0

Поскольку я уже использовал HKDF-Expand для генерации других материалов для ввода ключей, казалось естественным продолжить процесс и использовать его для создания валидатора. Однако я был бы рад услышать любые предлагаемые улучшения в формате. Хотя верно, что валидатор (или KCV) сам по себе может быть поврежден, он по-прежнему представляет собой значительную выгоду для больших сообщений, которые могут потребовать значительного времени для проверки HMAC, и для которых коррупция вне валидатора гораздо более вероятна (но неправильный пароль даже если * больше * наверняка). Валидатор также недвусмыслен, если он проверяет, но HMAC терпит неудачу. –

+0

Я доволен токеном проверки. И да, использование KDF для создания токена проверки является, очевидно, хорошей идеей, и тогда я бы, конечно, сам выбрал HKDF. Маркер проверки является лучшим именем, чем KCV, поскольку вычисление напрямую не связано с самим ключом. –

1

Нет, вы не можете, если не используете режим шифрования, который добавляет аутентификацию, такую ​​как режим работы GCM или EAX. В противном случае всегда есть странная вероятность того, что дешифрование вернет успех, поскольку дополнение может быть правильным после дешифрования. Другими словами, вы не можете использовать CCCryptorStatus (надежно) обнаружить неправильные ключи или испорченный зашифрованный текст. Как отметил Zaph, указывая на a discussion on the Apple forums, CCCryptorStatus никогда не может быть установлен на kCCDecodeError для новых версий iOS (6 и 7) из-за возможности дополнения атак Oracle.

Вместо использования шифрования, который добавляет аутентификацию, вы также можете добавить свой собственный тег аутентификации, например. путем вычисления значения HMAC по зашифрованному тексту. Лучше всего использовать второй ключ для HMAC и включить IV в аутентифицированные данные. Обратите внимание, что вам необходимо проверить тег аутентификации до, вы используете открытый текст или дешифруете последний блок (в режиме шифрования в режиме CBC). В противном случае вы будете уязвимы для атаки оракула.

Обратите внимание, что вы не сможете полностью различить неправильный ключ и повреждение зашифрованного текста.

+0

PS только взглянул на ваш код, поэтому не считайте это полным обзором. –

+2

В нем конкретно не проводится различие между этими двумя случаями. –

+0

@ Zaph Я никогда не говорил, что это индикатор, но я отредактировал вопрос, чтобы явным образом затронуть вашу проблему. –

0

По существу, нет способа узнать, правильно ли указано сообщение или неправильно расшифровано. Что касается AES (а также большинства шифров), то это всего лишь бит и бит. Это особенность.

Ошибка CCCryptorStatus обрабатывает только грубые ошибки. Он не устанавливает kCCDecodeError для неправильного заполнения, это подробно обсуждалось на форумах разработчиков Apple.

Определение того, является ли дешифрование правильным, является серьезной проблемой во Второй мировой войне с расшифровкой. По сути, была нужна «Crib», часть сообщения, которое, как известно, тестировало дешифрование. Из Википедии: термин «кроватка» возник в Bletchley Park, операции по расшифровке британской мировой войны.

Если вы хотите, чтобы вам нужно было добавить это к протоколу, который вы используете для связи.

+0

Использование «Шпаргалки» было, может быть, хорошей идеей на WW2, но не больше. Теги аутентификации (аутентифицированное шифрование, MAC или HMAC) теперь. –

+0

«Crib» использовалась для указания того, что дешифрование могло бы быть успешным. Это было знание части сообщения, такого как стандартный префикс или приветствие. Это была ** не ** аутентификация. – zaph

+0

Да, в этом проблема. С помощью только кроватки шифротекст уязвим для атаки оракула. Если он слишком мал, атакующий может попробовать, пока не получите правильную кроватку. И это только начало проблем ... –

 Смежные вопросы

  • Нет связанных вопросов^_^