Swift 3 Compiler Error : 'bytes' is unavailable: use withUnsafeBytes instead - ios

I have an encrypting method in Swift 2.2. How can I get bytes from data in Swift 3?
Here is my code:
func myEncrypt(encryptData:String) -> String? {
//security key must be 24charachters
let myKeyData : Data = "*******".data(using: String.Encoding.utf8)!
let myRawData : Data = encryptData.data(using: String.Encoding.utf8)!
let mykeydatamd5 = Data(bytes: myKeyData.bytes, count: 24)
let Crypto_status: CCCryptorStatus = CCCrypt(operation, algoritm, options, mykeydatamd5.bytes , keyLength, nil, myRawData.bytes, myRawData.count, buffer, buffer_size, &num_bytes_encrypted)
if UInt32(Crypto_status) == UInt32(kCCSuccess){
let myResult: Data = Data(bytes: buffer, count: num_bytes_encrypted)
free(buffer)
return myResult.base64EncodedString()
}else{
free(buffer)
return nil
}
}
The error occurs in myKeyData.bytes and myRawData.bytes.

As shown in the error message, you need to use withUnsafeBytes instead.
Generally, if you have some expression like this:
let result = someFunc(..., data.bytes, ...)
You need to rewrite it as:
let result = data.withUnsafeBytes {dataBytes in
someFunc(..., dataBytes, ...)
}
If you use multiple .bytes in a single expression, you may need to use nested withUnsafeBytes. In your case the related part of your code should be something like this:
let mykeydatamd5 = myKeyData.subdata(in: 0..<24) //Assuming myKeyData.count >= 24
let crypto_status: CCCryptorStatus = mykeydatamd5.withUnsafeBytes {mykeydataBytes in
myRawData.withUnsafeBytes {myRawDataBytes in
CCCrypt(operation, algoritm, options, mykeydataBytes, keyLength, nil, myRawDataBytes, myRawData.count, buffer, buffer_size, &num_bytes_encrypted)
}
}

Try this
func myEncrypt(encryptData:String) -> String? {
//security key must be 24charachters
var myKeyData = [UInt8]()
var myRawData = [UInt8]()
for char in "*******".utf8{
myKeyData += [char]
}
for char in encryptData.utf8{
myRawData += [char]
}
let mykeydatamd5 = Data(bytes: myKeyData, count: 24)
let Crypto_status: CCCryptorStatus = CCCrypt(operation, algoritm, options, mykeydatamd5.bytes , keyLength, nil, myRawData, myRawData.count, buffer, buffer_size, &num_bytes_encrypted)
if UInt32(Crypto_status) == UInt32(kCCSuccess){
let myResult: Data = Data(bytes: buffer, count: num_bytes_encrypted)
free(buffer)
return myResult.base64EncodedString()
}else{
free(buffer)
return nil
}
}
Hope it will help you

Related

How to pass key in HMAC as HEX Swift iOS

So I have this code to generate for HMAC-SHA1
let key = "foo".toSHA1()
let data = "bar"
var results = [CUnsignedChar](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, data, data.count, &results)
let hmacData:NSData = NSData(bytes: results, length: (Int(CC_SHA1_DIGEST_LENGTH)))
var bytes = [UInt8](repeating: 0, count: hmacData.length)
hmacData.getBytes(&bytes, length: hmacData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02hhx", UInt8(byte))
}
print(hexString)
and this code for converting key string to SHA1
func toSHA1() -> String {
let data = self.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02x", $0) }
return hexBytes.joined()
}
and the result is
faa3c04b058d38cecf1243421a596742a6cf1188
so using this onlineHMAC Generator outputs the same result. But my desired output should be
38b24d28d64f2459d42d1ecd1c9fa375ffeb369f
and I can achieve this by changing the Key type to HEX in the page that I provided.
So my problem now is how do I get the same output in my code? Do I need to convert key to hex?
Fixed it by passing digest as key instead of converting it to string.
Here's the updated code
let key = "foo".toSHA1()
let data = "bar"
var results = [CUnsignedChar](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), key, key.count, data, data.count, &results)
let hmacData:NSData = NSData(bytes: results, length: (Int(CC_SHA1_DIGEST_LENGTH)))
var bytes = [UInt8](repeating: 0, count: hmacData.length)
hmacData.getBytes(&bytes, length: hmacData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02hhx", UInt8(byte))
}
print(hexString)
func toSHA1() -> [UInt8] {
let data = self.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
return digest
}

Get IV and secret key from base64 string

I am working with crypto swift library But I have to write below logic in swift as I am very new in kotlin to understand the syntax.
Any leads would be greatly appreciated
fun decryptAES(data: ByteArray, secretKey: ByteArray): ByteArray {
try {
val byteBuffer = ByteBuffer.wrap(data)
val ivLength = byteBuffer.int
if (ivLength < 12 || ivLength >= 16) {
throw IllegalArgumentException("invalid iv length")
}
val iv = ByteArray(ivLength)
byteBuffer.get(iv)
val cipherText = ByteArray(byteBuffer.remaining())
byteBuffer.get(cipherText)
val encryptCipher = Cipher.getInstance("AES/GCM/PKCS5Padding")
encryptCipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(secretKey, "AES"), GCMParameterSpec(128, iv))
return encryptCipher.doFinal(cipherText)
} finally {
Arrays.fill(secretKey, 0.toByte())
}
}
After spending 2 days finally I am able to convert this code in Swift Hope this helps others
func decryptCode(_ cipher:String, _ key:String)-> String{
var keyBytes: [UInt8] = []
var codeBytes: [UInt8] = []
var code = ""
if let keyData = NSData(base64Encoded:key, options: .ignoreUnknownCharacters) {
keyBytes = [UInt8](keyData as Data)
}
if let codeData = NSData(base64Encoded: cipher, options: .ignoreUnknownCharacters) {
codeBytes = [UInt8](codeData as Data)
}
// First 4 bytes define the IV length
// next 12 to 16 bytes are reserve for IV
//and remaining bytes are actual cipher text
debugPrint(codeBytes)
let sizeOfIV = 4
let ivUInt8Array = Array([UInt8](codeBytes)[0 ..< sizeOfIV])
let ivLength:Int = Int(ivUInt8Array.reduce(0, +))
if ivLength < 12 || ivLength >= 16{
return code
}
let codeBytescount = [UInt8](codeBytes).count
let remainingBytes = Array([UInt8](codeBytes)[sizeOfIV ..< codeBytescount])
let remainingBytescount = [UInt8](remainingBytes).count
let iv = Array([UInt8](remainingBytes)[0 ..< ivLength])
let cipher = Array([UInt8](remainingBytes)[ivLength ..< remainingBytescount])
do{
let gcm = GCM(iv: iv, mode: .combined)
let aes = try AES(key: keyBytes, blockMode: gcm, padding: .pkcs5)
IFLOG("aes created")
let decrypted = try aes.decrypt(cipher)
IFLOG("decrypted completed")
if let decryptedString = String(bytes: decrypted, encoding: .utf8) {
code = decryptedString
}
debugPrint(code)
}catch let error as AES.Error {
debugPrint(error.localizedDescription)
return code
} catch {
return code
}
return code
}

How to convert data into little endian format?

var val = 1240;
convert into little endian formate swift 3
Ex: 1500 (0x5DC) to 0xDC050000
let value = UInt16(bigEndian: 1500)
print(String(format:"%04X", value.bigEndian)) //05DC
print(String(format:"%04X", value.littleEndian)) //DC05
Make sure you are actually using the bigEndian initializer.
With 32-bit integers:
let value = UInt32(bigEndian: 1500)
print(String(format:"%08X", value.bigEndian)) //000005DC
print(String(format:"%08X", value.littleEndian)) //DC050000
If you want 1500 as an array of bytes in little-endian order:
var value = UInt32(littleEndian: 1500)
let array = withUnsafeBytes(of: &value) { Array($0) }
If you want that as a Data:
let data = Data(array)
Or, if you really wanted that as a hex string:
let string = array.map { String(format: "%02x", $0) }.joined()
let timeDevide = self.setmiliSecond/100
var newTime = UInt32(littleEndian: timeDevide)
let arrayTime = withUnsafeBytes(of: &newTime)
{Array($0)}
let timeDelayValue = [0x0B] + arrayTime
You can do something like
//: Playground - noun: a place where people can play
import UIKit
extension String {
func hexadecimal() -> Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(location: 0, length: utf16.count)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard !data.isEmpty else { return nil }
return data
}
}
func convertInputValue<T: FixedWidthInteger>(_ inputValue: Data) -> T where T: CVarArg {
let stride = MemoryLayout<T>.stride
assert(inputValue.count % (stride / 2) == 0, "invalid pack size")
let fwInt = T.init(littleEndian: inputValue.withUnsafeBytes { $0.pointee })
let valuefwInt = String(format: "%0\(stride)x", fwInt).capitalized
print(valuefwInt)
return fwInt
}
var inputString = "479F"
var inputValue: Data! = inputString.hexadecimal()
let val: UInt16 = convertInputValue(inputValue) //9F47
inputString = "479F8253"
inputValue = inputString.hexadecimal()
let val2: UInt32 = convertInputValue(inputValue) //53829F47

What is the best way to write a struct to file?

I have this two structs:
struct pcap_hdr_s {
UInt32 magic_number;
UInt16 version_major;
UInt16 version_minor;
int32_t thiszone;
UInt32 sigfigs;
UInt32 snaplen;
UInt32 network;
};
//packet header
struct pcaprec_hdr_s {
UInt32 ts_sec;
UInt32 ts_usec;
UInt32 incl_len;
UInt32 orig_len;
};
which are initialised as follows(for example):
let pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number: 0xa1b2c3d4,
version_major: 2,
version_minor: 4,
thiszone: 0,
sigfigs: 0,
snaplen:
pcap_record_size,
network: LINKTYPE_ETHERNET)
let pcapRecHeader : pcaprec_hdr_s = pcaprec_hdr_s(ts_sec: UInt32(ts.tv_sec),
ts_usec: UInt32(ts.tv_nsec),
incl_len: plen,
orig_len: length)
I tried to create Data/NSData objects of the structs like this:
//write pcap header
let pcapHeaderData : NSData = NSData(bytes: pcapHeader, length: sizeofValue(pcapHeader))
//write pcaprec header
let pcapRecHeaderData : NSData = NSData(bytes: pcapRecHeader, length: sizeofValue(pcapRecHeader))
but I always get this error for each line:
"Connot convert value if type 'pcap_hdr_s' to expected arguemnt type 'UsafeRawPointer?'"
I had a look at the documentation of UnsafeRawPointers in Swift, but I don't get it enough as for now, to create the NSData object from the structs.
Am I on the right way or is there a better one to accomplish my intend?
If this Data initialisation would work, my next steps would be
Append pcapRecHeaderData to pcapHeaderData
write pcapHeaderData atomically to file/url with the provided function of Data/NSData
EDIT:
//packet ethernet header
struct ethernet_hdr_s {
let dhost : [UInt8]
let shost : [UInt8]
let type : UInt16
};
let src_mac : [UInt8] = [0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB]
let dest_mac : [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
let ethernetHeader : ethernet_hdr_s = ethernet_hdr_s(dhost: dest_mac, shost: src_mac, type: 0x0800)
EDIT 2:
let payloadSize = packet.payload.count
let plen = (payloadSize < Int(pcap_record_size) ? payloadSize : Int(pcap_record_size));
bytesWritten = withUnsafePointer(to: &(packet.payload)) {
$0.withMemoryRebound(to: UInt8.self, capacity: Int(plen)) {
ostream.write($0, maxLength: Int(plen))
}
}
if bytesWritten != (Int(plen)) {
// Could not write all bytes, report error ...
NSLog("error in Writting packet payload, not all Bytes written: bytesWritten: %d|plen: %d", bytesWritten, Int(plen))
}
You can write arbitrary data to an InputStream without creating a
(NS)Data object first. The "challenge" is how to convert the pointer to
the struct to an UInt8 pointer as expected by the write method:
let ostream = OutputStream(url: url, append: false)! // Add error checking here!
ostream.open()
var pcapHeader = pcap_hdr_s(...)
let headerSize = MemoryLayout.size(ofValue: pcapHeader)
let bytesWritten = withUnsafePointer(to: &pcapHeader) {
$0.withMemoryRebound(to: UInt8.self, capacity: headerSize) {
ostream.write($0, maxLength: headerSize)
}
}
if bytesWritten != headerSize {
// Could not write all bytes, report error ...
}
In the same way you can read data from in InputStream:
let istream = InputStream(url: url)! // Add error checking here!
istream.open()
let bytesRead = withUnsafeMutablePointer(to: &pcapHeader) {
$0.withMemoryRebound(to: UInt8.self, capacity: headerSize) {
istream.read($0, maxLength: headerSize)
}
}
if bytesRead != headerSize {
// Could not read all bytes, report error ...
}
If the file was possibly created on a different platform with a
different byte order then you can check the "magic" and swap bytes
if necessary (as described on https://wiki.wireshark.org/Development/LibpcapFileFormat):
switch pcapHeader.magic_number {
case 0xa1b2c3d4:
break // Already in host byte order
case 0xd4c3b2a1:
pcapHeader.version_major = pcapHeader.version_major.byteSwapped
pcapHeader.version_minor = pcapHeader.version_minor.byteSwapped
// ...
default:
// Unknown magic, report error ...
}
To simplify the task of writing and reading structs one can define
custom extension methods, e.g.
extension OutputStream {
enum ValueWriteError: Error {
case incompleteWrite
case unknownError
}
func write<T>(value: T) throws {
var value = value
let size = MemoryLayout.size(ofValue: value)
let bytesWritten = withUnsafePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: size) {
write($0, maxLength: size)
}
}
if bytesWritten == -1 {
throw streamError ?? ValueWriteError.unknownError
} else if bytesWritten != size {
throw ValueWriteError.incompleteWrite
}
}
}
extension InputStream {
enum ValueReadError: Error {
case incompleteRead
case unknownError
}
func read<T>(value: inout T) throws {
let size = MemoryLayout.size(ofValue: value)
let bytesRead = withUnsafeMutablePointer(to: &value) {
$0.withMemoryRebound(to: UInt8.self, capacity: size) {
read($0, maxLength: size)
}
}
if bytesRead == -1 {
throw streamError ?? ValueReadError.unknownError
} else if bytesRead != size {
throw ValueReadError.incompleteRead
}
}
}
Now you can write and read simply with
try ostream.write(value: pcapHeader)
try istream.read(value: &pcapHeader)
Of course this works only with "self-contained" structs like your
pcap_hdr_s and pcaprec_hdr_s.
You can convert pcap_hdr_s to Data and vice versa in Swift 3 with
pcap_hdr_s -> Data
var pcapHeader : pcap_hdr_s = pcap_hdr_s(magic_number ...
let data = withUnsafePointer(to: &pcapHeader) {
Data(bytes: UnsafePointer($0), count: MemoryLayout.size(ofValue: pcapHeader))
}
Data -> pcap_hdr_s
let header: pcap_hdr_s = data.withUnsafeBytes { $0.pointee }
Reference: round trip Swift number types to/from Data

Swift language: How to call SecRandomCopyBytes

From Objective-C, I could do this:
NSMutableData *data = [NSMutableData dataWithLength:length];
int result = SecRandomCopyBytes(kSecRandomDefault, length, data.mutableBytes);
When attempting this in Swift, I have the following:
let data = NSMutableData(length: Int(length))
let result = SecRandomCopyBytes(kSecRandomDefault, length, data.mutableBytes)
but I get this compiler error:
'Void' is not identical to 'UInt8'
The data.mutableBytes parameter is rejected because the types do not match, but I can't figure out how to coerce the parameter (and I'm presuming it's somehow safe to do).
This appears to work:
let data = NSMutableData(length: Int(length))
let result = SecRandomCopyBytes(kSecRandomDefault, length, UnsafeMutablePointer<UInt8>(data.mutableBytes))
Swift 5
let count: Int = <byteCount>
var data = Data(count: count)
let result = data.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, count, $0.baseAddress!)
}
Swift 4:
var data = Data(count: <count>)
let result = data.withUnsafeMutableBytes { mutableBytes in
SecRandomCopyBytes(kSecRandomDefault, data.count, mutableBytes)
}
Swift 4 version:
let count = 16
var data = Data(count: count)
_ = data.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, count, $0)
}

Resources