Расшифровать SJCL зашифрованный текст в Objective-C
Я получаю данные, зашифрованные с помощью SJCL, в приложение для iOS, где мне нужно их расшифровать. Другой конец использует SJCL с режимом AES CCM, который не поддерживается Apple CommonCrypto, поэтому для этого я использую библиотеку VPCCMCrypt. Невозможно внести какие-либо изменения в другую сторону, отправляющую текст шифра SJCL.
Вот мои методы для расшифровки:
+ (NSData *)decryptSjcl:(NSDictionary *)cipher password:(NSString *)password {
if (cipher == nil || password == nil) {
return nil;
}
int version = [cipher[@"v"] intValue];
NSString *iv = cipher[@"iv"];
uint iter = [cipher[@"iter"] unsignedIntValue];
uint ks = [cipher[@"ks"] unsignedIntValue];
uint ts = [cipher[@"ts"] unsignedIntValue];
NSString *mode = cipher[@"mode"];
NSString *adata = cipher[@"adata"];
NSString *algorithm = cipher[@"cipher"];
NSString *salt = cipher[@"salt"];
NSString *ct = cipher[@"ct"];
if (version != 1 || ! [@"aes" isEqualToString:algorithm]) {
return nil;
}
NSData *rawIv = [[NSData alloc] initWithBase64EncodedString:iv options:0];
NSData *rawSalt = [[NSData alloc] initWithBase64EncodedString:salt options:0];
NSData *rawAdata = [[NSData alloc] initWithBase64EncodedString:adata options:0];
NSData *cipherData = [[NSData alloc] initWithBase64EncodedString:ct options:0];
NSData *key;
NSMutableData *decipheredData = nil;
if ([@"ccm" isEqualToString:mode]) {
key = [Cryptor sjclAesKeyForPassword:password salt:rawSalt iterations:iter keySize:ks];
decipheredData = [Cryptor decryptAesCcmData:cipherData iv:rawIv key:key adata:rawAdata tagSize:ts];
}
return decipheredData;
}
Генерация ключей SJCL:
+ (NSData *)sjclAesKeyForPassword:(NSString *)password salt:(NSData *)salt iterations:(uint)iterations keySize:(uint)keySizeBits {
NSMutableData *derivedKey = [NSMutableData dataWithLength:keySizeBits / 8];
int result = CCKeyDerivationPBKDF(kCCPBKDF2,
password.UTF8String,
[password lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
salt.bytes,
salt.length,
kCCPRFHmacAlgSHA256,
iterations,
derivedKey.mutableBytes,
derivedKey.length);
NSAssert(result == kCCSuccess, @"Unable to create AES key for password: %d", result);
return derivedKey;
}
AES CCM расшифровка:
+ (NSMutableData *)decryptAesCcmData:(NSData *)cipherData iv:(NSData *)iv key:(NSData *)key adata:(NSData *)adata tagSize:(uint)tagSizeBits {
VPCCMCrypt *ccm = [[VPCCMCrypt alloc] initWithKey:key
iv:iv
adata:adata
tagLength:tagSizeBits / 8];
[ccm decryptDataWithData:cipherData
finishedBlock:^(NSData *data) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Decrypted SJCL message: %@", result);
}
errorBlock:^(NSError *error) {
NSLog(@"ERROR");
}];
return nil;
}
Все входные данные из SJCL правильно анализируются (IV, соль, размер ключа, размер тега, итерации PBKDF, зашифрованный текст) и декодируются в NSData
от их Base64
закодированное представление. Пароль используется одинаково. Все данные, включая созданный ключ AES, не равны нулю.
В конце он терпит неудачу внутри VPCCMCrypt
проверка тэга CCM (тэги разные). Что-то не так с кодом выше? Есть ли какая-либо другая библиотека iOS/Objective-C/Swift для расшифровки AES CCM или даже лучше SJCL? Я не заинтересован в обертке JavaScript библиотеки SJCL.
Для тестирования я использую простые зашифрованные данные с демонстрационной страницы SJCL.
РЕДАКТИРОВАТЬ:
Как сказано в комментариях, SJCL отправляет 16 байтов IV вместо максимум 12 байтов для режима CCM и затем внутренне ограничивает его до максимум 12 байтов при расшифровке. Вот обновленный метод расшифровки:
+ (NSData *)decryptSjcl:(NSDictionary *)cipher password:(NSString *)password {
if (cipher == nil || password == nil) {
return nil;
}
int version = [cipher[@"v"] intValue];
NSString *iv = cipher[@"iv"];
uint iter = [cipher[@"iter"] unsignedIntValue];
uint ks = [cipher[@"ks"] unsignedIntValue];
uint ts = [cipher[@"ts"] unsignedIntValue];
NSString *mode = cipher[@"mode"];
NSString *adata = cipher[@"adata"];
NSString *algorithm = cipher[@"cipher"];
NSString *salt = cipher[@"salt"];
NSString *ct = cipher[@"ct"];
if (version != 1 || ! [@"aes" isEqualToString:algorithm]) {
return nil;
}
NSMutableData *rawIv = [[NSMutableData alloc] initWithBase64EncodedString:iv options:0];
NSMutableData *rawSalt = [[NSMutableData alloc] initWithBase64EncodedString:salt options:0];
NSMutableData *rawAdata = [[NSMutableData alloc] initWithBase64EncodedString:adata options:0];
NSMutableData *cipherData = [[NSMutableData alloc] initWithBase64EncodedString:ct options:0];
NSData *key;
NSData *decipheredData = nil;
if ([@"ccm" isEqualToString:mode]) {
key = [Cryptor sjclAesKeyForPassword:password salt:rawSalt iterations:iter keySize:ks];
// Clamp the SJCL IV - They use a 16 byte IV for CCM mode which is against specification and they do a funky
// clamping. CCM mode IV should be max 13 bytes long. They almost always clamp it to 13 bytes but save the whole
// 16 bytes in their JSON container.
// for (L=2; L<4 && ol >>> 8*L; L++) {}
// if (L < 15 - ivl) { L = 15-ivl; }
// iv = w.clamp(iv,8*(15-L));
int64_t ivl = rawIv.length;
int64_t ol = cipherData.length - (ts / 8);
int64_t L = 2;
for (L = 2; L < 4 && ol >> 8*L; L++) {}
if (L < 15 - ivl) {
L = 15 - ivl;
}
NSRange subrange = NSMakeRange(0, (NSUInteger)(15 - L));
decipheredData = [Cryptor decryptAesCcmData:cipherData iv:[rawIv subdataWithRange:subrange] key:key adata:rawAdata tagSize:ts];
}
else {
decipheredData = nil;
}
return decipheredData;
}
Последнее, что не хватает, это проверка TAG, я не могу этого сделать. Есть идеи?
1 ответ
SJCL не использует весь (128/192/256-битный) IV в режиме AES-CCM, но демонстрационная страница показывает это. Попробуйте это с меньшим количеством байтов.
Здесь вы можете узнать, как работает расчет длины IV: http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_ccm.js.html
for (L=2; L<4 && ol >>> 8*L; L++) {}
if (L < 15 - ivl) { L = 15-ivl; }
iv = w.clamp(iv,8*(15-L));