0

I tried implement AES 256 CTR mode for decrypt files in Swift 4 with IDZSwiftCommonCrypto CommonCrypto's wrapper. The problem is that I get 'unimplemented' as return status, despite it looks like this mode is implemented in CommonCrypto.

import Foundation
import IDZSwiftCommonCrypto

let tmp = NSTemporaryDirectory() as NSString
let encryptedFilePath = Bundle.main.path(forResource: "encrypted", ofType: "pdfenc")
var decryptedFilePath = "\(tmp)test.pdf"

var encryptedFileInputStream = InputStream(fileAtPath: encryptedFilePath)!
var decryptedFileOutputStream = OutputStream(toFileAtPath: decryptedFilePath, append:false)!

let keyK = "4c7b3180a4dca6ece2d051811a7b9ca23f210e28ceabd92c5d171e567875f70b" // Key used to encrypt the file
let tkeyK = arrayFrom(hexString: keyK)

let iv = Array<UInt8>() // It's far less secure to not use an IV but it's not necessary 
let bufferSize = 1024
let sc = StreamCryptor(operation: StreamCryptor.Operation.decrypt, algorithm: StreamCryptor.Algorithm.aes, mode: StreamCryptor.Mode.CTR, padding: StreamCryptor.Padding.NoPadding, key: tkeyK, iv: iv)


var inputBuffer = Array<UInt8>(repeating:0, count:1024)
var outputBuffer = Array<UInt8>(repeating:0, count:1024)
encryptedFileInputStream.open()
decryptedFileOutputStream.open()

var cryptedBytes : Int = 0

while encryptedFileInputStream.hasBytesAvailable {
    let bytesRead = encryptedFileInputStream.read(&inputBuffer, maxLength: inputBuffer.count)
    let status = sc.update(bufferIn: inputBuffer, byteCountIn: bytesRead, bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
    assert(status == Status.success)
    if(cryptedBytes > 0) {
        let bytesWritten = decryptedFileOutputStream.write(outputBuffer, maxLength: Int(cryptedBytes))
        assert(bytesWritten == Int(cryptedBytes))
    }
}

let status = sc.final(bufferOut: &outputBuffer, byteCapacityOut: outputBuffer.count, byteCountOut: &cryptedBytes)
assert(status == Status.success)
if(cryptedBytes > 0) {
    let bytesWritten = decryptedFileOutputStream.write(outputBuffer, maxLength: Int(cryptedBytes))
    assert(bytesWritten == Int(cryptedBytes))
}

encryptedFileInputStream.close()
decryptedFileOutputStream.close()

See this GitHub repo for more informations about testing this code.

I have to decrypt the file (on the client) that have been encrypted on the server with Node.js.

alessionossa
  • 923
  • 2
  • 15
  • 41
  • 1. The code does not show the counter (IV) value. 2. Sure seems more code that should be necessary, do you really need a stream? – zaph Dec 07 '17 at 16:57
  • @zaph There is no IV because the encryption it's not necessary for my purpose (I only need to not allow a user to open a PDF). I want to use a stream so that I can decrypt large files without worrying about memory usage. – alessionossa Dec 07 '17 at 17:03
  • 1
    CTR mode requires an initial counter value and that many times is specified in an IV parameter. See [CTR mode](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)). Additionally there is a requirement that the same counter value and key combination never be used more than once. – zaph Dec 07 '17 at 17:05
  • @zaph I've added an IV but I still get the error. – alessionossa Dec 07 '17 at 17:21
  • 1. Yes, Common Crypto supports CTR mode. 2. The counter value must be the same one used during encryption. 3. The key is in hex and represents 16 binary bytes (128-bits) yet you are specifying a 256-bit key (in the question title), you need to provide a full length key or what ever bytes follow the key (`tkeyK`) will be used to fill out the 256-bits. 4. Provide test values including the contents of a short file. IOW a [mcve]. – zaph Dec 07 '17 at 18:33
  • @zaph I have edited the question. Meanwhile I contacted the Framework creator and he told me that he will investigate, the problems comes from CommonCrypto. – alessionossa Dec 08 '17 at 09:52
  • Of course for encrypting a file the other option is to use the more common CBC mode and PKCS#7 padding with a random IV. See RNCryptor [Incremental Usage](https://github.com/RNCryptor/RNCryptor#incremental-usage) – zaph Dec 08 '17 at 12:45
  • WRT IDZSwiftCommonCrypto I have serious concerns with implememtation choices and if the developers fully understand security. The fact that they are zero padding short keys is disturbing compounded by the fact that information is not in the documentation. – zaph Dec 08 '17 at 13:21
  • Here is a video where the Framework writer explain how he build it https://academy.realm.io/posts/danny-keogan-swift-cryptography/ – alessionossa Dec 08 '17 at 13:49
  • You need to provide an IV array of 16 bytes, not just a pointer with no contents: `let iv = Array()`. The `StreamCryptor` call expects a pointer to an array of the correct size and with valid contents, IDZSwiftCommonCrypto does not verify any of that and just passes `ivBuffer: UnsafeRawPointer`. The IV which is really the CTR value must be created properly, IDZSwiftCommonCrypto does not help with this. Typically the CTR will have a MS unique section followed by a 0x00 portion that is large enough to contain the maximum number of blocks that will be encrypted. – zaph Dec 08 '17 at 13:56
  • As Keogan states in the video IDZSwiftCommonCrypto is just a thin wrapper around Common Crypto, it provided no help to use Common Crypto correctly. It does not even check the arguments. Use RNCryptor, it is a complete secure system that has been substantially peer reviewed. – zaph Dec 08 '17 at 14:05

0 Answers0