How to convert a file into a Array<UInt8> to encrypt in cryptoSwift? - swift

I'm trying to encrypt a file in Swift with the framework Cryptoswift. I made it, but with files a little bit heavy like mp4, mp3, it is very slow. I don't really know what it is happening, if I'm implementing in the wrong way or the algorithm is like that.
Here is my code.
do {
// write until all is written
let ex = "a"
func writeTo(stream: OutputStream, bytes: Array<UInt8>) {
var writtenCount = 0
while stream.hasSpaceAvailable && writtenCount < bytes.count {
writtenCount += stream.write(bytes, maxLength: bytes.count)
}
}
let path = "somewhere"
let aes = try AES(key: key, iv: iv)
var encryptor = aes.makeEncryptor()
// prepare streams
//let data = Data(bytes: (0..<100).map { $0 })
let inputStream = InputStream(fileAtPath: path)
let outputStream = OutputStream(toFileAtPath: "somewhere", append: false)
inputStream?.open()
outputStream?.open()
var buffer = Array<UInt8>(repeating: 0, count: 2)
// encrypt input stream data and write encrypted result to output stream
while (inputStream?.hasBytesAvailable)! {
let readCount = inputStream?.read(&buffer, maxLength: buffer.count)
if (readCount! > 0) {
try encryptor.update(withBytes: buffer[0..<readCount!]) { (bytes) in
writeTo(stream: outputStream!, bytes: bytes)
}
}
}
// finalize encryption
try encryptor.finish { (bytes) in
writeTo(stream: outputStream!, bytes: bytes)
}
if let ciphertext = outputStream?.property(forKey: Stream.PropertyKey(rawValue: Stream.PropertyKey.dataWrittenToMemoryStreamKey.rawValue)) as? Data {
print("Encrypted stream data: \(ciphertext.toHexString())")
}
} catch {
print(error)
}

I'd say, try to re-test with the Release build. Assuming you're looking at Debug build, it is expected that Swift code will be significantly slower

Related

How to translate Python HMAC Request into Swift

I've been at this about 10 hours now and no matter what HMAC combination I use for swift I can not get it to match the key generated by python.
Python Code:
signature = hmac.new(secret.decode('hex'), msg=datastring, digestmod=hashlib.sha256).hexdigest()
Swift Code:
let key = SymmetricKey(data: self.secret.data(using: .utf8)!)
let hexData = HMAC<SHA256>.authenticationCode(for: datastring.data(using: .utf8)!, using: key)
let signature = Data(hexData).map { String(format: "%02hhx", $0) }.joined()
Any help with what I'm doing wrong (or missing) in Swift would be greatly appreciated.
Based on the assumption that self.secret is a String containing the hex representation of the secret key, the difference between the two comes down to your use of:
self.secret.data(using: .utf8)!
which will just perform a straight conversion to the underlying bytes instead of converting each character pair into the corresponding byte, as:
secret.decode('hex')
does in Python 2.
From what I can tell, there isn't a function to do this conversion in the Swift standard library, but you could do it with something like:
func bytes(fromHex input: String) -> Data {
var result = Data()
var byte: UInt8 = 0
for (index, character) in input.enumerated() {
let codeUnit = character.utf8[character.utf8.startIndex]
var nibble: UInt8 = 0
switch codeUnit {
case 0x30..<0x3a:
nibble = codeUnit - 0x30
case 0x61..<0x67:
nibble = codeUnit - 0x57
default:
break
}
if index % 2 == 0 {
byte |= (nibble << 4)
} else {
byte |= nibble
result.append(contentsOf: [byte])
byte = 0
}
}
return result
}
and then your code would become:
let key = SymmetricKey(data: bytes(fromHex: self.secret))
let hexData = HMAC<SHA256>.authenticationCode(for: datastring.data(using: .utf8)!, using: key)
let signature = Data(hexData).map { String(format: "%02hhx", $0) }.joined()

CryptoSwift - Getting nil when using makeEncryptor() for string of less than 16 bytes in iOS

I am using cryptoSwift library for encryption and decryption. But it's working with only string 16 bytes. If i am passing small string or less than 16 bytes then getting nil result.
I am using Incremental operations use instance of Cryptor and encrypt/decrypt one part at a time.
Please help me here, is there anything which i am doing wrong.
Thanks in advance.
func encAndDec(){
do {
// Encryption start
let data = Data.init(base64Encoded: "12345678901234567890123456789012".base64Encoded()!)
let iv : Array<UInt8> = [0,0,0,0,0,0,0,0,0,0,0,0]
let nIv = Data(iv)
let gcmEnc = GCM(iv: nIv.bytes, mode: .detached)
var enc = try? AES(key: data!.bytes, blockMode: gcmEnc, padding: .noPadding).makeEncryptor()
let arrStr = ["My name is tarun"] // Working
//let arrStr = ["tarun"] // Not working for this string
var ciphertext = Array<UInt8>()
for txt in arrStr{
let ciperText = try? enc?.update(withBytes: Array(txt.utf8)) // Getting nil for small string.
ciphertext.append(contentsOf: ciperText!)
}
var res = try? enc?.finish()
gcmEnc.authenticationTag = self.randomGenerateBytes(count: 16)?.bytes
res?.append(contentsOf: (gcmEnc.authenticationTag)!)
let cipherData = Data(ciphertext) + Data(res!)
let strEnc = String(decoding: cipherData, as: UTF8.self)
print(strEnc)
// Decryption start from here
do {
let gcmDec = GCM.init(iv: nIv.bytes, additionalAuthenticatedData: nil, tagLength: 16, mode: .detached)
var aesDec = try! AES(key: data!.bytes, blockMode: gcmDec, padding: .noPadding).makeDecryptor()
let tag_length = 16
let encData = cipherData.subdata(in: 0..<cipherData.count - tag_length)
let tag = cipherData.subdata(in: encData.count ..< cipherData.count)
let decData = try? aesDec.update(withBytes: encData.bytes) //Getting nil here for small string
let strData = String(decoding: decData!, as: UTF8.self)
print(strData)
do{
var res = try? aesDec.finish(withBytes: tag.bytes)
res?.append(contentsOf: tag)
}catch{
}
} catch {
// failed
}
}
}
func randomGenerateBytes(count: Int) -> Data? {
let bytes = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: 1)
defer { bytes.deallocate() }
let status = CCRandomGenerateBytes(bytes, count)
guard status == kCCSuccess else { return nil }
return Data(bytes: bytes, count: count)
}
There's nothing wrong with aes-256-gcm implementation in CryptoSwift, as some of the commenters have suggested, you just have some bugs in your code. Hopefully the following will help you out.
I'm just going to call it GCM below for brevity.
GCM encryption takes as input the plaintext, a key, and an initialization vector and produces ciphertext and an authentication tag. In your code, you set the authentication tag to random bytes, overwriting the authentication tag.
I think it's a bit clearer if you break your code up into some functions, each with a clearly defined purpose. I also stripped away some of the conversions from Data to and from [UInt8] for clarity.
Here's what the encryption function would look like:
func enc(plainText: [String], key: [UInt8], iv: [UInt8]) throws -> (cipherText: [UInt8], authenticationTag: [UInt8]?)
{
let gcmEnc = GCM(iv: iv, mode: .detached)
var enc = try AES(key: key, blockMode: gcmEnc, padding: .noPadding).makeEncryptor()
var ciphertext = Array<UInt8>()
for txt in plainText {
ciphertext += try enc.update(withBytes: Array(txt.utf8))
}
ciphertext += try enc.finish()
return (ciphertext, gcmEnc.authenticationTag)
}
When you're decrypting GCM you need to pass in the ciphertext, key, initialization vector and the authentication tag. That would look like this:
func dec(cipherText: [UInt8], authenticationTag: [UInt8]?, key: [UInt8], iv: [UInt8]) throws -> [UInt8]? {
let tagLength = authenticationTag?.count ?? 0
let gcmDec = GCM.init(iv: iv, additionalAuthenticatedData: nil, tagLength: tagLength, mode: .detached)
gcmDec.authenticationTag = authenticationTag
var aesDec = try AES(key: key, blockMode: gcmDec, padding: .noPadding).makeDecryptor()
var decData = try aesDec.update(withBytes: cipherText)
decData += try aesDec.finish()
return decData
}
In both cases, you need to make sure that you append the output of the finish call to the ciphertext or plaintext. This is particularly important with small amounts of data as the update method may produce nothing!
With these two functions written you can rewrite your test function as follows:
func encAndDec(){
do {
guard let key = Data.init(base64Encoded: "12345678901234567890123456789012".base64Encoded()!)
else {
fatalError("Failed to create key")
}
let iv : Array<UInt8> = [0,0,0,0,
0,0,0,0,
0,0,0,0]
//let arrStr = ["My name is tarun"] // Working
let arrStr = ["tarun"] // Not working for this string
let (cipherText, authenticationTag) = try enc(plainText: arrStr, key: key.bytes, iv: iv)
guard let decrypedPlainText = try dec(cipherText: cipherText,
authenticationTag: authenticationTag, key: key.bytes, iv: iv) else {
fatalError("Decryption return nil")
}
guard let decryptedString = String(bytes: decrypedPlainText, encoding: .utf8) else {
fatalError("Failed to convert plaintext to string using UTF8 encoding.")
}
print("Decrypted Plaintext: \(decryptedString)")
}
catch let e {
print("EXCEPTION: \(e)")
}
}
If you run this you'll find it produces the expected output.
The complete example code can be found at: https://gist.github.com/iosdevzone/45456d2911bf2422bc4a6898876ba0ab
I don't believe GCM requires PADDING. Here is an example pretty much straight from the NODE.JS documentation that works fine and does not use padding. The line below will show the Ciphertext length is 5
I have done the same with Ruby, Java, Go, and others and none require padding or the input value to be a multiple of 16 bytes like the Swift library seems to require. Anyone else help confirm this is a bug in Swift implementation of GCM?
const crypto = require('crypto');
const key = '12345678901234567890123456789012';
const iv = '000000000000'
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const plaintext = 'Hello';
const ciphertext = cipher.update(plaintext, 'utf8');
**console.log("ciphertext length %d", ciphertext.length)**
cipher.final();
const tag = cipher.getAuthTag();
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(tag);
const receivedPlaintext = decipher.update(ciphertext, null, 'utf8');
try {
decipher.final();
} catch (err) {
console.error('Authentication failed!');
return;
}
console.log(receivedPlaintext);

AudioKit, exporting AVAudioPCMBuffer array to audio file with fade in/out

I'm capturing audio from AKLazyTap and rendering the accumulated [AVAudioPCMBuffer] to an audio file, in the background, while my app's audio is running. This works great, but I want to add fade in/out to clean up the result. I see the convenience extension for adding fades to a single AVAudioPCMBuffer, but I'm not sure how I'd do it on an array. I'd thought to concatenate the buffers, but there doesn't appear to be support for that. Does anyone know if that's currently possible? Basically it would require something similar to copy(from:readOffset:frames), but would need to have a write offset as well...
Or maybe there's an easier way?
UPDATE
Okay, after studying some related AK code, I tried directly copying buffer data over to a single, long buffer, then applying the fade convenience function. But this gives me an empty (well, 4k) file. Is there some obvious error here that I'm just not seeing?
func renderBufferedAudioToFile(_ audioBuffers: [AVAudioPCMBuffer], withStartOffset startOffset: Int, endOffset: Int, fadeIn: Float64, fadeOut: Float64, atURL url: URL) {
// strip off the file name
let name = String(url.lastPathComponent.split(separator: ".")[0])
var url = self.module.stateManager.audioCacheDirectory
// UNCOMPRESSED
url = url.appendingPathComponent("\(name).caf")
let format = Conductor.sharedInstance.sourceMixer.avAudioNode.outputFormat(forBus: 0)
var settings = format.settings
settings["AVLinearPCMIsNonInterleaved"] = false
// temp buffer for fades
let totalFrameCapacity = audioBuffers.reduce(0) { $0 + $1.frameLength }
guard let tempAudioBufferForFades = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: totalFrameCapacity) else {
print("Failed to create fade buffer!")
return
}
// write ring buffer to file.
let file = try! AVAudioFile(forWriting: url, settings: settings)
var writeOffset: AVAudioFrameCount = 0
for i in 0 ..< audioBuffers.count {
var buffer = audioBuffers[i]
let channelCount = Int(buffer.format.channelCount)
if i == 0 && startOffset != 0 {
// copy a subset of samples in the buffer
if let subset = buffer.copyFrom(startSample: AVAudioFrameCount(startOffset)) {
buffer = subset
}
} else if i == audioBuffers.count - 1 && endOffset != 0 {
if let subset = buffer.copyTo(count: AVAudioFrameCount(endOffset)) {
buffer = subset
}
}
// write samples into single, long buffer
for i in 0 ..< buffer.frameLength {
for n in 0 ..< channelCount {
tempAudioBufferForFades.floatChannelData?[n][Int(i + writeOffset)] = (buffer.floatChannelData?[n][Int(i)])!
}
}
print("buffer \(i), writeOffset = \(writeOffset)")
writeOffset = writeOffset + buffer.frameLength
}
// update!
tempAudioBufferForFades.frameLength = totalFrameCapacity
if let bufferWithFades = tempAudioBufferForFades.fade(inTime: fadeIn, outTime: fadeOut) {
try! file.write(from: bufferWithFades)
}
}

Swift 4 Int32 to [UInt8]

I am trying to get the bytes from an integer into an [UInt8] to send them over a wire protocol. While I found answers that work for Swift 2/3, none of the solutions work for Swift 4.
The following snippet works to encode a message for small message sizes (just the raw string data prepended with a network byte order Int32 size):
func send(message: String) {
let messageSize = message.utf8.count
let encodedMessageSize = Int32(messageSize).bigEndian
let frameSize = messageSize + 4
var buffer: [UInt8] = Array()
buffer.append(0)
buffer.append(0)
buffer.append(0)
buffer.append(UInt8(messageSize))
buffer.append(contentsOf: message.utf8)
outputStream.write(buffer, maxLength: frameSize)
}
I have also tried using raw pointers directly, but cannot get anything to work for Swift 4 along that avenue either.
The overall tasks is to encode and frame messages that consist of integers and strings. The encoding converts everything to strings and adds a null at the end of each string. The framing simply prepends the message with a network byte order Int32 size. I cannot change the protocol, but am willing to consider other approaches to achieving this end.
cheers,
[EDIT] Updated code using #MartinR's code (with #Hamish's suggestion). Also made some progress of the overall task in the mean time.
func encodeMessagePart(_ message: String) -> [UInt8] {
var buffer: [UInt8] = Array(message.utf8)
buffer.append(0)
return buffer
}
func encodeMessagePart(_ message: Int) -> [UInt8] {
return encodeMessagePart("\(message)")
}
func frameMessage(_ buffer: [UInt8]) -> [UInt8] {
let bufferSize = buffer.count
var encodedBufferSize = Int32(bufferSize).bigEndian
let encodedBufferSizeData = withUnsafeBytes(of: &encodedBufferSize) { Data($0) }
var frame: [UInt8] = Array()
frame.append(contentsOf: encodedBufferSizeData)
frame.append(contentsOf: buffer)
return frame
}
func sendMessage(_ buffer: [UInt8]) {
let frame = frameMessage(buffer)
outputStream.write(frame, maxLength: frame.count)
}
func sendMessage(_ message: String) {
let encodedPart = encodeMessagePart(message)
sendMessage(encodedPart)
}
// func sendMessage(_ messages: Encodable...) {
// var buffer: [UInt8] = Array()
// for message in messages {
// let b = encodeMessagePart(message)
// buffer.append(contentsOf: b)
// }
// sendMessage(buffer)
// }
You can create a Data value from the integer with
let encodedMessageSize = Int32(messageSize).bigEndian
let data = withUnsafeBytes(of: encodedMessageSize) { Data($0) }
(In Swift versions before 4.2 you'll have to write
var encodedMessageSize = Int32(messageSize).bigEndian
let data = withUnsafeBytes(of: &encodedMessageSize) { Data($0) }
instead.)
The data can then be appended to the array with
buffer.append(contentsOf: data)
Alternatively you can use a data buffer instead of an array:
func send(message: String) {
let messageSize = message.utf8.count
let encodedMessageSize = Int32(messageSize).bigEndian
var data = withUnsafeBytes(of: encodedMessageSize) { Data($0) }
data.append(Data(message.utf8))
let amountWritten = data.withUnsafeBytes { [count = data.count] in
outputStream.write($0, maxLength: count)
}
}
Finally note that that the write() method might write less bytes than
provided (e.g. on network connections), so you should always check
the return value.

How to cast decrypted UInt8 to String?

I am using CryptoSwift to encrypt data. I am learning how to use it however I cannot get past the first basic tutorial. I am unable to convert the encrypted data back to a String - which kind of defeats the purpose of encrypting it in the first place if I cannot legibly decrypt the data
Code:
let string = "Hi. This is Atlas"
let input: [UInt8] = Array(string.utf8)
print(input)
let key: [UInt8] = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
let iv: [UInt8] = AES.randomIV(AES.blockSize)
do {
let encryptedBytes: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).encrypt(input, padding: PKCS7())
print(encryptedBytes)
let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encryptedBytes, padding: PKCS7())
print(decrypted) // << need to convert this array of byted to a string (should be equal to original input)
} catch {
} catch {
}
Thank you for the help
You'll want Foundation to decode the UTF8 for you since there's no way to generate a String.UTF8View directly. So convert to NSData first.
let decrypted: [UInt8] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]
let data = NSData(bytes: decrypted, length: decrypted.count)
let str = String(data: data, encoding: NSUTF8StringEncoding)
If you want to do it without Foundation, you can, but it's a little work. You have to manage the decoding yourself.
extension String {
init?(utf8Bytes: [UInt8]) {
var decoder = UTF8()
var g = utf8Bytes.generate()
var characters: [Character] = []
LOOP:
while true {
let result = decoder.decode(&g)
switch result {
case .Result(let scalar): characters.append(Character(scalar))
case .EmptyInput: break LOOP
case .Error: return nil
}
}
self.init(characters)
}
}
let unicode = String(utf8Bytes: bytes)
(I'm very surprised that this isn't built into Swift stdlib since it's so common and can be quickly built out of other parts of Swift stdlib. Often when that's the case, there's a reason that I'm just not aware of yet, so there may be some subtle problem with my approach here.)
let stringDecrypted = String(decrypted.map { Character(UnicodeScalar($0)) })
So it maps each UInt8 to UnicodeScalar and then to Character. After that it uses String's initializer to create String from array of Characters.