Swift 5 + kCCDecrypt (CommonCrypto): не удается расшифровать - PullRequest
0 голосов
/ 03 апреля 2019

Попытка написать свои собственные функции шифрования / дешифрования в Swift 5, основанные на множестве других подобных вопросов - и безуспешно.

Я использую CommonCrypto + CCCrypt для шифрования / дешифрования (AES, 256 ключей, случайный iv).

Я предпочитаю NSData.bytes над withUnsafeBytes (что просто слишком запутанно в Swift 5).

Моя encrypt функция выглядит следующим образом:

func encrypt(_ string: String) throws -> Data {
    guard let dataToEncrypt: Data = string.data(using: .utf8) else {
        throw AESError.stringToDataFailed
    }

    // Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
    let dataToEncryptNSData = NSData(data: dataToEncrypt)

    let bufferSize: Int = ivSize + dataToEncryptNSData.length + kCCBlockSizeAES128
    let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: bufferSize)
    defer { buffer.deallocate() }

    let status: Int32 = SecRandomCopyBytes(
        kSecRandomDefault,
        kCCBlockSizeAES128,
        buffer
    )
    guard status == 0 else {
        throw AESError.generateRandomIVFailed
    }

    var numberBytesEncrypted: Int = 0

    let cryptStatus: CCCryptorStatus = CCCrypt( // Stateless, one-shot encrypt operation
        CCOperation(kCCEncrypt),                // op: CCOperation
        CCAlgorithm(kCCAlgorithmAES),           // alg: CCAlgorithm
        options,                                // options: CCOptions
        key.bytes,                              // key: the "password"
        key.length,                             // keyLength: the "password" size
        buffer,                                 // iv: Initialization Vector
        dataToEncryptNSData.bytes,              // dataIn: Data to encrypt bytes
        dataToEncryptNSData.length,             // dataInLength: Data to encrypt size
        buffer + kCCBlockSizeAES128,            // dataOut: encrypted Data buffer
        bufferSize,                             // dataOutAvailable: encrypted Data buffer size
        &numberBytesEncrypted                   // dataOutMoved: the number of bytes written
    )

    guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
        throw AESError.encryptDataFailed
    }

    return Data(bytes: buffer, count: numberBytesEncrypted + ivSize)
}

Функция decrypt:

func decrypt(_ data: Data) throws -> String {

    // Seems like the easiest way to avoid the `withUnsafeBytes` mess is to use NSData.bytes.
    let dataToDecryptNSData = NSData(data: data)

    let bufferSize: Int = dataToDecryptNSData.length - ivSize
    let buffer = UnsafeMutablePointer<NSData>.allocate(capacity: bufferSize)
    defer { buffer.deallocate() }

    var numberBytesDecrypted: Int = 0

    let cryptStatus: CCCryptorStatus = CCCrypt(         // Stateless, one-shot encrypt operation
        CCOperation(kCCDecrypt),                        // op: CCOperation
        CCAlgorithm(kCCAlgorithmAES128),                // alg: CCAlgorithm
        options,                                        // options: CCOptions
        key.bytes,                                      // key: the "password"
        key.length,                                     // keyLength: the "password" size
        dataToDecryptNSData.bytes,                      // iv: Initialization Vector
        dataToDecryptNSData.bytes + kCCBlockSizeAES128, // dataIn: Data to decrypt bytes
        bufferSize,                                     // dataInLength: Data to decrypt size
        buffer,                                         // dataOut: decrypted Data buffer
        bufferSize,                                     // dataOutAvailable: decrypted Data buffer size
        &numberBytesDecrypted                           // dataOutMoved: the number of bytes written
    )

    guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
        throw AESError.decryptDataFailed
    }

    let decryptedData = Data(bytes: buffer, count: numberBytesDecrypted)

    guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
        throw AESError.dataToStringFailed
    }

    return decryptedString
}

Они основаны на этом удивительном ответе от пользователя "@zaph".

Хотя encrypt, кажется, работает, decrypt не удается.

Эта строка специально:

guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
    throw AESError.dataToStringFailed
}

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

Вот пастбина со всем кодом, который вы можете скопировать / вставить в игровую площадку и нажать Play. Swift 5 требуется: https://pastebin.com/raw/h6gacaHX

Обновление
Сейчас я следую предложенному @ OOper подходу. Окончательный код можно посмотреть здесь:
https://github.com/backslash-f/aescryptable

1 Ответ

2 голосов
/ 03 апреля 2019

На самом деле, использование Data.withUnsafeBytes является своего рода беспорядком в Swift 5, хотя NSData.bytes не может быть самым простым способом , так как его использование иногда, казалось бы, работает, а иногда нет.

Вам нужно привыкнуть работать с Data.withUnsafeBytes, если вы хотите написать всегда работающий код на Swift с Data.

struct AES {
    private let key: Data //<- Use `Data` instead of `NSData`

    private let ivSize: Int                     = kCCBlockSizeAES128
    private let options: CCOptions              = CCOptions(kCCOptionPKCS7Padding)

    init(keyString: String) throws {
        guard keyString.count == kCCKeySizeAES256 else {
            throw AESError.invalidKeySize
        }
        guard let keyData: Data = keyString.data(using: .utf8) else {
            throw AESError.stringToDataFailed
        }
        self.key = keyData
    }
}

extension AES: Cryptable {

    func encrypt(_ string: String) throws -> Data {
        guard let dataToEncrypt: Data = string.data(using: .utf8) else {
            throw AESError.stringToDataFailed
        }

        let bufferSize: Int = ivSize + dataToEncrypt.count + kCCBlockSizeAES128
        var buffer = Data(count: bufferSize)

        let status: Int32 = buffer.withUnsafeMutableBytes {bytes in
            SecRandomCopyBytes(
                kSecRandomDefault,
                kCCBlockSizeAES128,
                bytes.baseAddress!
            )
        }
        guard status == 0 else {
            throw AESError.generateRandomIVFailed
        }

        var numberBytesEncrypted: Int = 0

        let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
            dataToEncrypt.withUnsafeBytes {dataBytes in
                buffer.withUnsafeMutableBytes {bufferBytes in
                    CCCrypt( // Stateless, one-shot encrypt operation
                        CCOperation(kCCEncrypt),                // op: CCOperation
                        CCAlgorithm(kCCAlgorithmAES),           // alg: CCAlgorithm
                        options,                                // options: CCOptions
                        keyBytes.baseAddress,                   // key: the "password"
                        key.count,                              // keyLength: the "password" size
                        bufferBytes.baseAddress,                // iv: Initialization Vector
                        dataBytes.baseAddress,                  // dataIn: Data to encrypt bytes
                        dataToEncrypt.count,                    // dataInLength: Data to encrypt size
                        bufferBytes.baseAddress! + kCCBlockSizeAES128, // dataOut: encrypted Data buffer
                        bufferSize,                             // dataOutAvailable: encrypted Data buffer size
                        &numberBytesEncrypted                   // dataOutMoved: the number of bytes written
                    )
                }
            }
        }

        guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
            throw AESError.encryptDataFailed
        }

        return buffer[..<(numberBytesEncrypted + ivSize)]
    }

    func decrypt(_ data: Data) throws -> String {

        let bufferSize: Int = data.count - ivSize
        var buffer = Data(count: bufferSize)

        var numberBytesDecrypted: Int = 0

        let cryptStatus: CCCryptorStatus = key.withUnsafeBytes {keyBytes in
            data.withUnsafeBytes {dataBytes in
                buffer.withUnsafeMutableBytes {bufferBytes in
                    CCCrypt(         // Stateless, one-shot encrypt operation
                        CCOperation(kCCDecrypt),                        // op: CCOperation
                        CCAlgorithm(kCCAlgorithmAES128),                // alg: CCAlgorithm
                        options,                                        // options: CCOptions
                        keyBytes.baseAddress,                           // key: the "password"
                        key.count,                                      // keyLength: the "password" size
                        dataBytes.baseAddress,                          // iv: Initialization Vector
                        dataBytes.baseAddress! + kCCBlockSizeAES128,    // dataIn: Data to decrypt bytes
                        bufferSize,                                     // dataInLength: Data to decrypt size
                        bufferBytes.baseAddress,                        // dataOut: decrypted Data buffer
                        bufferSize,                                     // dataOutAvailable: decrypted Data buffer size
                        &numberBytesDecrypted                           // dataOutMoved: the number of bytes written
                    )
                }
            }
        }

        guard cryptStatus == CCCryptorStatus(kCCSuccess) else {
            throw AESError.decryptDataFailed
        }

        let decryptedData = buffer[..<numberBytesDecrypted]

        guard let decryptedString = String(data: decryptedData, encoding: .utf8) else {
            throw AESError.dataToStringFailed
        }

        return decryptedString
    }

}
...