why cannot read the receipt data for on-device validation - swift

I am using the following code to read the receipt data. I can successfully validate the receipt signature by using OpenSSL static library 1.1.1k
private func readReceipt(_ receiptPKCS7: UnsafeMutablePointer<PKCS7>?) {
// Get a pointer to the start and end of the ASN.1 payload
let receiptSign = receiptPKCS7?.pointee.d.sign
let octets = receiptSign?.pointee.contents.pointee.d.data
var ptr = UnsafePointer(octets?.pointee.data)
let end = ptr!.advanced(by: Int(octets!.pointee.length))
var type: Int32 = 0
var xclass: Int32 = 0
var length: Int = 0
ASN1_get_object(&ptr, &length, &type, &xclass, ptr!.distance(to: end))
guard type == V_ASN1_SET else {
status = .unexpectedASN1Type
return
}
// 1
while ptr! < end {
// 2
ASN1_get_object(&ptr, &length, &type, &xclass, ptr!.distance(to: end))
guard type == V_ASN1_SEQUENCE else {
status = .unexpectedASN1Type
return
}
// 3 type
guard let attributeType = readASN1Integer(ptr: &ptr, maxLength: length) else {
status = .unexpectedASN1Type
return
}
print("shark-IAP, ", attributeType)
// 4 version
guard let _ = readASN1Integer(ptr: &ptr, maxLength: ptr!.distance(to: end)) else {
print("shark-IAP, 3")
status = .unexpectedASN1Type
return
}
// 5 value
ASN1_get_object(&ptr, &length, &type, &xclass, ptr!.distance(to: end))
guard type == V_ASN1_OCTET_STRING else {
print("shark-IAP, 4")
status = .unexpectedASN1Type
return
}
switch attributeType {
case 2: // The bundle identifier
var stringStartPtr = ptr
bundleIdString = readASN1String(ptr: &stringStartPtr, maxLength: length)
bundleIdData = readASN1Data(ptr: ptr!, length: length)
case 3: // Bundle version
var stringStartPtr = ptr
bundleVersionString = readASN1String(ptr: &stringStartPtr, maxLength: length)
case 4: // Opaque value
let dataStartPtr = ptr!
opaqueData = readASN1Data(ptr: dataStartPtr, length: length)
case 5: // Computed GUID (SHA-1 Hash)
let dataStartPtr = ptr!
hashData = readASN1Data(ptr: dataStartPtr, length: length)
case 12: // Receipt Creation Date
var dateStartPtr = ptr
receiptCreationDate = readASN1Date(ptr: &dateStartPtr, maxLength: length)
case 17: // IAP Receipt
var iapStartPtr = ptr
let parsedReceipt = IAPReceipt(with: &iapStartPtr, payloadLength: length)
if let newReceipt = parsedReceipt {
inAppReceipts.append(newReceipt)
}
case 19: // Original App Version
var stringStartPtr = ptr
originalAppVersion = readASN1String(ptr: &stringStartPtr, maxLength: length)
case 21: // Expiration Date
var dateStartPtr = ptr
expirationDate = readASN1Date(ptr: &dateStartPtr, maxLength: length)
default: // Ignore other attributes in receipt
print("Not processing attribute type: \(attributeType)")
}
// Advance pointer to the next item
ptr = ptr!.advanced(by: length)
} // end while
}
func readASN1Integer(ptr: inout UnsafePointer<UInt8>?, maxLength: Int) -> Int? {
var type: Int32 = 0
var xclass: Int32 = 0
var length: Int = 0
ASN1_get_object(&ptr, &length, &type, &xclass, maxLength)
guard type == V_ASN1_INTEGER else {
print("shark-IAP no!", type)
return nil
}
// let integerObject = c2i_ASN1_INTEGER(nil, &ptr, length)
let integerObject = d2i_ASN1_UINTEGER(nil, &ptr, length)
let intValue = ASN1_INTEGER_get(integerObject)
ASN1_INTEGER_free(integerObject)
return intValue
}
I got these print outputs. I suspect the function readASN1Integer is wrong. Maybe c2i_ASN1_INTEGER will be fine but this is deprecated in OpenSSL 1.1*, that d2i_ASN1_UINTEGER is used instead. And d2i_ASN1_UINTEGER needs to pass (identifier + length/octet + content), not just the content. In ASN1_get_object, the pointer has changed position. So d2i_ASN1_UINTEGER reads wrong. The first readASN1Integer causes bias that the second readASN1Integer throws error.
shark-IAP, 0
shark-IAP no! 8
shark-IAP, 3
shark-IAP, bundleVersionString nil
shark-IAP, expirationData nil
shark-IAP, 0
shark-IAP no! 8
shark-IAP, 3
shark-IAP, bundleVersionString nil
shark-IAP, expirationData nil
But I dont' know how to adjust the code to suit d2i_ASN1_UINTEGER. Thank you for your help!

Stackoverflow is a place to suppress devils.
I found the solution. I modified the readASN1Integer to this
func readASN1Integer(ptr: inout UnsafePointer<UInt8>?, maxLength: Int) -> Int? {
var type: Int32 = 0
var xclass: Int32 = 0
var length: Int = 0
let save_ptr = ptr
ASN1_get_object(&ptr, &length, &type, &xclass, maxLength)
guard type == V_ASN1_INTEGER else {
return nil
}
// let integerObject = c2i_ASN1_INTEGER(nil, &ptr, length)
ptr = save_ptr
let integerObject = d2i_ASN1_UINTEGER(nil, &ptr, maxLength)
let intValue = ASN1_INTEGER_get(integerObject)
ASN1_INTEGER_free(integerObject)
return intValue
}
Since the pointer position is changed, need to set it back, that's why save_ptr comes (referenced from c2i_ASN1_INTEGER function in Openssl 1.1.0)
d2i_ASN1_UINTEGER(nil, &ptr, length) is changed to d2i_ASN1_UINTEGER(nil, &ptr, maxLength). For every place, including in readReceipt, length should be maxLength or ptr!.distance(to: end).

Related

List all processes with arguments in Swift

I'm trying to convert some piece of code to Swift that will list all running processes. But since it requires calling some C specific APIs I'm a bit struggling.
Can someone tell me here what am I doing here incorrectly? print statement at the end is outputting incorrect values. I assume it should be process name. Also line info = malloc(length) gives me the creeps. How should I properly allocate it?
var maxArgumentsSize: Int = 0
if maxArgumentsSize == 0 {
var size: size_t = MemoryLayout<Int>.size
var mib: [Int32] = [CTL_KERN, KERN_ARGMAX]
let a = sysctl(&mib, 2, &maxArgumentsSize, &size, nil, 0)
if a == -1 {
maxArgumentsSize = 4096;
}
}
var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_ALL]
var info: UnsafeMutableRawPointer? = nil
var length: size_t = 0
var count: Int = 0
if sysctl(&mib, 3, nil, &length, nil, 0) < 0 {
exit(EXIT_FAILURE)
}
info = malloc(length)
if sysctl(&mib, 3, info, &length, nil, 0) < 0 {
free(info)
exit(EXIT_FAILURE)
}
count = length / MemoryLayout<kinfo_proc>.size
for index in 0...count {
let info = info?.assumingMemoryBound(to: kinfo_proc.self)
let pid: pid_t = info![index].kp_proc.p_pid
let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: maxArgumentsSize)
var mib: [Int32] = [CTL_KERN, KERN_PROCARGS2, pid]
if sysctl(&mib, 3, buffer, &maxArgumentsSize, nil, 0) == 0 {
let str = String(cString: buffer, encoding: .utf8)
print(str)
}
free(buffer);
}
Basically I've changed my initial code to this and calling #MartinR solution (https://stackoverflow.com/a/72445381/1187415) at the end. Of course it's not complete and pasted from my code directly but it's working.
// Get all processess information:
var name: [CInt] = [CTL_KERN, KERN_PROC, KERN_PROC_ALL]
var length: size_t = 0
if sysctl(&name, CUnsignedInt(name.count), nil, &length, nil, 0) < 0 {
return
}
var infoPtr = UnsafeMutableRawPointer.allocate(
byteCount: length,
alignment: MemoryLayout<kinfo_proc>.alignment
)
if sysctl(&name, CUnsignedInt(name.count), infoPtr, &length, nil, 0) < 0 {
infoPtr.deallocate()
return
}
let count = length / MemoryLayout<kinfo_proc>.size
for index in 0...count {
let info = infoPtr.assumingMemoryBound(to: kinfo_proc.self)
let pid: pid_t = info[index].kp_proc.p_pid
let arguments = self.processArguments(pid: pid)
}
infoPtr.deallocate()

Appending and extracting objects from NSMutableData

I need to create create a data stream that contains multiple parameters, send it over the network and then extract those parameter when I receive the data.
This is how and create my data (I'm certain all of my variables contain a value)
let dataToSend = NSMutableData()
var mType = Int32(messageType.rawValue)
var reqId = Int32(requestId)
dataToSend.appendDataWithUnsafeBytes(from: &mType, of: Int32.self)
dataToSend.appendDataWithUnsafeBytes(from: &reqId, of: Int32.self)
/* extra protocol data length. In version 0, this is0 as thereis no extra data.
In the future, if you need to add extra protocol data use this*/
var unUsedProtocol = Int32(0)
dataToSend.appendDataWithUnsafeBytes(from: &unUsedProtocol, of: Int32.self)
var encodedDataJson = !jsonString.isEmptyOrNil ? jsonString?.asciiValues : [UInt8]()
dataToSend.appendDataWithUnsafeBytes(from: &encodedDataJson, of: [UInt8]?.self)
var bData = bindaryData
dataToSend.appendDataWithUnsafeBytes(from: &bData, of: Data.self)
here is my appendDataWithUnsafeBytes NSMutableData extension.
extension NSMutableData {
func appendDataWithUnsafeBytes<T>(from element: inout T, of type: T.Type) {
let size = MemoryLayout.size(ofValue: element)
withUnsafeBytes(of: &element) { ptr in
let buffer = ptr.bindMemory(to: type)
if let address = buffer.baseAddress {
self.append(address, length: size)
} else {
VsLogger.logDebug("appendDataWithUnsafeBytes", "unable to get base address of pointer of type: \(type)")
}
}
}
}
and this is how try to extract it (I get the index value along with the data)
var messageTypeValue: Int32? = nil
var requestId: Int32? = nil
var encodedJsonData: Data? = nil
var binaryData: Data? = nil
let intSize = MemoryLayout<Int32>.size
let dataSize = MemoryLayout<Data>.size
var offset = index
bufferData.getBytes(&messageTypeValue, range: NSRange(location: offset, length: intSize))
offset += intSize //8
bufferData.getBytes(&requestId, range: NSRange(location: offset, length: intSize))
offset += intSize //12
/*skipping extra bytes (unsuedProtocol in sendMessageFunction). They come from a future version
that this code doesn't understand*/
offset += intSize //16
bufferData.getBytes(&encodedJsonData, range: NSRange(location: offset, length: dataSize))
offset += dataSize //32
bufferData.getBytes(&binaryData, range: NSRange(location: offset, length: dataSize))
I'm only able to get the first value (messageTypeValue) but for the rest I either get nil or not the right data.
Thank you!
***** UPDATE *****
I got it working by modifying my sending and receiving functions as follows. Where I send it.
let dataToSend = NSMutableData()
var mType = Int32(messageType.rawValue)
var reqId = Int32(requestId)
dataToSend.appendDataWithUnsafeBytes(from: &mType, of: Int32.self)
dataToSend.appendDataWithUnsafeBytes(from: &reqId, of: Int32.self)
/* estra protocol data length. In version 0, this is0 as thereis no extra data.
In the future, if you need to add extra protocol data use this*/
var unUsedProtocol = Int32(0)
dataToSend.appendDataWithUnsafeBytes(from: &unUsedProtocol, of: Int32.self)
var jsonData = Data(!jsonString.isEmptyOrNil ? jsonString!.asciiValues : [UInt8]())
dataToSend.appendDataWithUnsafeBytes(from: &jsonData, of: Data.self)
var bData = bindaryData
dataToSend.appendDataWithUnsafeBytes(from: &bData, of: Data.self)
where I receive it
var offset = Int(index)
let int32Size = MemoryLayout<Int32>.size
let dataSize = MemoryLayout<Data>.size
let messageTypeValue = (bufferData.bytes + offset).load(as: Int32.self)
offset += int32Size
let requestId = (bufferData.bytes + offset).load(as: Int32.self)
offset += int32Size
//skip this one since it not used
//let unusedProtocol = (bufferData.bytes + offset).load(as: Int32.self)
offset += int32Size
let encodedJsonData = (bufferData.bytes + offset).load(as: Data.self)
offset += dataSize
let binaryData = bufferData.bytes.load(fromByteOffset: offset, as: Data.self)
the data is supposed to always be align properly, but is there a way to do some error checking on bufferData.bytes.load(fromByteOffset:as:)
As already mentioned in the comments I highly recommend to use native Data.
First of all you need MartinR's Data extension to convert numeric types to Data and vice versa. I added an custom append method
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
mutating func append<T>(_ other: T) {
append(.init(from: other))
}
}
This is a very simple version of your data set, three Int32 values and a JSON array
let mType : Int32 = 1
let reqId : Int32 = 2
let unUsedProtocol : Int32 = 0
let json = try! JSONEncoder().encode(["hello", "world"])
For convenience I omitted the error handling. In production code try! is discouraged.
The huge benefit of Data is that it can be treated as a collection type, an array of (UInt8) bytes. To build the Data package convert the numeric values and append the bytes respectively
var dataToSend = Data()
dataToSend.append(mType)
dataToSend.append(reqId)
dataToSend.append(unUsedProtocol)
dataToSend.append(json)
On the receiver side to extract the numeric values back this is a helper function which increments also the current index (as inout type), the byte length arises from the type. The function throws an error if the index is out of range and if the type cannot be converted.
enum ConvertDataError : Error { case outOfRange, invalidType}
func extractNumber<T : ExpressibleByIntegerLiteral>(from data : Data, type: T.Type, startIndex: inout Int) throws -> T {
let endIndex = startIndex + MemoryLayout<T>.size
guard endIndex <= data.endIndex else { throw ConvertDataError.outOfRange }
let subdata = data[startIndex..<endIndex]
guard let resultType = subdata.to(type: type) else { throw ConvertDataError.invalidType }
startIndex = endIndex
return resultType
}
Get the start index of the data package and extract the Int32 values
var index = dataToSend.startIndex
do {
let mType1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
let reqId1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
let unUsedProtocol1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
print(mType1, reqId1, unUsedProtocol1)
For the json part you need the length, in this example 17 bytes
let jsonLength = json.count
let jsonOffset = index
index += jsonLength
let json1 = try JSONDecoder().decode([String].self, from: dataToSend[jsonOffset..<index])
print(json1)
} catch {
print(error)
}

Swift “UDP Read” code - unsaferawbufferpointer compile error

I’m sure someone can solve this in seconds but I’m very new to swift, using playgrounds on the iPad. I’m trying to modify some SendUDP code to recieve instead, but I can’t solve the compile error (unsafepointer is not convertible to unsaferawbufferpointer) on the readResult= line. The SEND works fine with very similar code, but I’m really struggling here, way out of my depth...
Here’s the code
func readUDP() {
guard
let addresses =
try ? addressesFor(host: "192.168.4.1", port: 80)
else {
print("host not found")
return
}
if addresses.count != 1 {
print("host ambiguous; using the first one")
}
address = addresses[0]
fd1 = socket(Int32(address.ss_family), SOCK_DGRAM, 0)
guard fd1 >= 0
else {
print("`socket` failed`")
return
}
defer {
let junk = close(fd1)
assert(junk == 0)
}
var message = [UInt8](repeating: 0, count: 1024)
let messageCount = message.count
var readResult = message.withUnsafeBytes {
(messagePtr: UnsafePointer < UInt8 > ) - > Int in
return address.withSockAddr {
(sa, saLen) - > Int in
return recvfrom(fd1, messagePtr, messageCount, 0, sa, & saLen)
}
}
guard readResult >= 0
else {
print("read failed")
return
}
print("success")
}
You can use Swift's implicit bridging to simplify to something like this:
var message = [UInt8](repeating: 0, count: 1024)
let messageCount = message.count
var readResult = address.withSockAddr {
(sa, saLen) - > Int in
return recvfrom(fd1, &message, messageCount, 0, sa, &saLen)
}
guard readResult >= 0
else {
print("read failed")
return
}

How to parse Hex with String, Float

I have binary files which containing names of place and coordinates ( latitude, longitude ), whenever I parse it to String using encoding .ascii it won't parse it well. I assume that parsing from Float values (coordinates) failing.
Reading InputStream
extension Data {
init(reading input: InputStream) {
self.init()
input.open()
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
while input.hasBytesAvailable {
let read = input.read(buffer, maxLength: bufferSize)
self.append(buffer, count: read)
}
buffer.deallocate()
input.close()
}
}
File to parse
let filepath = Bundle.main.path(forResource: "MN", ofType: "dat")
let data = Data.init(reading: InputStream(fileAtPath: filepath)!)
let parsedData = String.init(data: data, encoding: .ascii)
Any ideas how could I parse it in correct way ?
For example Java ObjectInputStream have methods called:
inputStreamObj.readUTF()
inputStreamObj.readFloat()
Java
As I wrote in the comment, you need to read the spec Object Serialization Stream Protocol.
So, first 4 bytes represents STREAM_MAGIC, STREAM_VERSION, expected to be always the same value. And 5 byte sequence 0x7A 0xhh 0xhh 0xhh 0xhh represents TC_BLOCKDATALONG(0xhhhhhhhh).
And all blocks needs to be concatenated before parsing strings and floats.
So, preparing the DataReader:
(Nearly the same as Sulthan's, but this treats Modified UTF-8 correctly.)
struct DataReader {
enum DataReaderError: Error {
case invalidFirstByte(byte: UInt16, offset: Int)
case invalidFollowingByte
case missingFollowingByte
case insufficientData
}
var data: Data
var currentPosition: Int
init(data: Data) {
self.data = data
self.currentPosition = 0
}
mutating func skipBytes(_ n: Int) {
currentPosition += n
}
private mutating func readBigEndian<T: FixedWidthInteger>() throws -> T {
guard currentPosition + MemoryLayout<T>.size <= data.count else {
throw DataReaderError.insufficientData
}
var fixedWithInteger: T = 0
let range: Range<Int> = currentPosition ..< currentPosition + MemoryLayout<T>.size
withUnsafeMutableBytes(of: &fixedWithInteger) {ptrT in
let uint8Ptr = ptrT.baseAddress!.assumingMemoryBound(to: UInt8.self)
data.copyBytes(to: uint8Ptr, from: range)
}
currentPosition += MemoryLayout<T>.size
return fixedWithInteger.bigEndian
}
mutating func readFloat() throws -> Float {
let floatBits: UInt32 = try readBigEndian()
return Float(bitPattern: floatBits)
}
mutating func readUnsignedShort() throws -> Int {
let ushortValue: UInt16 = try readBigEndian()
return Int(ushortValue)
}
mutating func readInt() throws -> Int {
let intValue: Int32 = try readBigEndian()
return Int(intValue)
}
mutating func readUnsignedByte() throws -> Int {
guard currentPosition < data.count else {
throw DataReaderError.insufficientData
}
let byte = data[currentPosition]
currentPosition += 1
return Int(byte)
}
mutating func readBytes(_ n: Int) throws -> Data {
guard currentPosition + n <= data.count else {
throw DataReaderError.insufficientData
}
let subdata = data[currentPosition ..< currentPosition+n]
currentPosition += n
return subdata
}
mutating func readUTF() throws -> String {
//Get byte size of the string
let count = try readUnsignedShort()
//Decoding Modified UTF-8
var utf16: [UInt16] = []
var offset = 0
while offset < count {
let firstByte = UInt16(data[currentPosition + offset])
if firstByte & 0b1_0000000 == 0b0_0000000 {
utf16.append(firstByte)
offset += 1
} else if firstByte & 0b111_00000 == 0b110_00000 {
guard offset + 1 < count else {throw DataReaderError.missingFollowingByte}
let secondByte = UInt16(data[currentPosition + offset + 1])
guard secondByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
let codeUnit = ((firstByte & 0b000_11111) << 6) | (secondByte & 0b00_111111)
utf16.append(codeUnit)
offset += 2
} else if firstByte & 0b1111_0000 == 0b1110_0000 {
guard offset + 2 < count else {throw DataReaderError.missingFollowingByte}
let secondByte = UInt16(data[currentPosition + offset + 1])
guard secondByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
let thirdByte = UInt16(data[currentPosition + offset + 2])
guard thirdByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
let codeUnit = ((firstByte & 0b0000_1111) << 12) | ((secondByte & 0b00_111111) << 6) | (thirdByte & 0b00_111111)
utf16.append(codeUnit)
offset += 3
} else {
throw DataReaderError.invalidFirstByte(byte: firstByte, offset: currentPosition+offset)
}
}
currentPosition += offset
return String(utf16CodeUnits: &utf16, count: utf16.count)
}
var isAtEnd: Bool {
return currentPosition == data.count
}
}
We can parse your MN.dat as follows:
let mnUrl = Bundle.main.url(forResource: "MN", withExtension: "dat")!
do {
let data = try Data(contentsOf: mnUrl)
var reader = DataReader(data: data)
reader.skipBytes(4)
//First collect all blocks
var blockData = Data()
while !reader.isAtEnd {
let contentType = try reader.readUnsignedByte()
if contentType == 0x7A {//TC_BLOCKDATALONG
let size = try reader.readInt()
let block = try reader.readBytes(size)
blockData.append(block)
} else if contentType == 0x77 {//TC_BLOCKDATA
let size = try reader.readUnsignedByte()
let block = try reader.readBytes(size)
blockData.append(block)
} else {
print("Unsupported content type")
break
}
}
//Then read the contents of blockData
var blockReader = DataReader(data: blockData)
while !blockReader.isAtEnd {
let string = try blockReader.readUTF()
print(string)
let float1 = try blockReader.readFloat()
print(float1)
let float2 = try blockReader.readFloat()
print(float2)
//Use string, float1, float2 as you like
}
} catch {
print(error)
}
Output:
Albert Lea
43.648
-93.3683
Albertville
45.2377
-93.6544
Alexandria
45.8852
-95.3775
(... no errors...)
Woodbury
44.9239
-92.9594
Worthington
43.62
-95.5964
Wyoming
45.3364
-92.9972
Zimmerman
45.4433
-93.59
You may need to modify the code above if your binary data may contain other content types.
I will show you how to parse Java-encoded data. However, since I cannot understand the format of the file, the response will not be complete:
First, load the file:
// load the file
let fileUrl = URL(fileURLWithPath: "/Users/sulthan/Downloads/MN.dat")
let data = try! Data(contentsOf: fileUrl)
Second, create a simple Java data reader:
// create a simple data reader
class Reader {
let data: Data
private var offset = 0
init(data: Data) {
self.data = data
}
var hasMoreData: Bool {
return offset < data.count
}
func skip(length: Int) {
offset += length
}
func readByte() -> UInt8 {
defer { offset += 1}
return data[offset]
}
// java bytes are unsigned
func readJavaByte() -> Int8 {
return Int8(bitPattern: readByte())
}
func readBytes(length: Int) -> Data {
defer { offset += length }
return data.subdata(in: offset ..< offset + length)
}
private func readJavaUShort() -> UInt16 {
let byte1 = UInt16(exactly: readByte())!
let byte2 = UInt16(exactly: readByte())!
return (byte1 << 8) | byte2
}
func readJavaShort() -> Int16 {
return Int16(bitPattern: readJavaUShort())
}
// Java UTF-8 encodes the length as first two bytes (unsigned java short)
func readJavaUtf() -> String? {
let length = readJavaUShort()
let data = readBytes(length: Int(length))
return String(data: data, encoding: .utf8)
}
private func readUInt32() -> UInt32 {
let short1 = UInt32(exactly: readJavaUShort())!
let short2 = UInt32(exactly: readJavaUShort())!
return (short1 << 16) | short2
}
func readJavaInt() -> Int32 {
let short1 = Int32(exactly: readJavaShort())!
let short2 = Int32(exactly: readJavaShort())!
return (short1 << 16) | short2
}
// interpret the 4 bytes as a floating point number
func readJavaFloat() -> Float {
let bits = readUInt32()
return Float(bitPattern: bits)
}
}
Third, parse the data. I cannot do this completely since the data format is unknown:
// create a reader from our data
let reader = Reader(data: data)
// some data I don't understand
reader.skip(length: 4)
var offset = 0
while reader.hasMoreData {
// some data I don't understand in the beginning and after every 52 items
if offset % 53 == 0 {
reader.skip(length: 5)
}
print(reader.readJavaUtf())
print(reader.readJavaFloat())
print(reader.readJavaFloat())
offset += 1
}
The data parsing will crash with the provided data after some items are parsed. I am assuming you know how to handle that since you know the format.

How do i convert HexString To ByteArray in Swift 3

I'm was trying to convert hexString to Array of Bytes([UInt8]) I searched everywhere but couldn't find a solution. Below is my swift 2 code
func stringToBytes(_ string: String) -> [UInt8]? {
let chars = Array(string)
let length = chars.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
for var i = 0; i < length; i += 2 {
if let a = find(hexChars, chars[i]),
let b = find(hexChars, chars[i+1]) {
bytes.append(UInt8(a << 4) + UInt8(b))
} else {
return nil
}
}
return bytes
}
Example Hex
Hex : "7661706f72"
expectedOutput : "vapor"
This code can generate the same output as your swift 2 code.
func stringToBytes(_ string: String) -> [UInt8]? {
let length = string.characters.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
var index = string.startIndex
for _ in 0..<length/2 {
let nextIndex = string.index(index, offsetBy: 2)
if let b = UInt8(string[index..<nextIndex], radix: 16) {
bytes.append(b)
} else {
return nil
}
index = nextIndex
}
return bytes
}
let bytes = stringToBytes("7661706f72")
print(String(bytes: bytes!, encoding: .utf8)) //->Optional("vapor")
Here is a sketch of how I'd do it in a more idiomatic Swift style (this might be Swift 4 only):
func toPairsOfChars(pairs: [String], string: String) -> [String] {
if string.count == 0 {
return pairs
}
var pairsMod = pairs
pairsMod.append(String(string.prefix(2)))
return toPairsOfChars(pairs: pairsMod, string: String(string.dropFirst(2)))
}
func stringToBytes(_ string: String) -> [UInt8]? {
// omit error checking: remove '0x', make sure even, valid chars
let pairs = toPairsOfChars(pairs: [], string: string)
return pairs.map { UInt8($0, radix: 16)! }
}
Following code may be help for you
extension String {
/// Create `Data` from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a `Data` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
///
/// - returns: Data represented by this hexadecimal string.
func hexadecimal() -> Data? {
var data = Data(capacity: characters.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard data.count > 0 else {
return nil
}
return data
}
}
extension String {
/// Create `String` representation of `Data` created from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a String object from that. Note, if the string has any spaces, those are removed. Also if the string started with a `<` or ended with a `>`, those are removed, too.
init?(hexadecimal string: String) {
guard let data = string.hexadecimal() else {
return nil
}
self.init(data: data, encoding: .utf8)
}
/// - parameter encoding: The `NSStringCoding` that indicates how the string should be converted to `NSData` before performing the hexadecimal conversion.
/// - returns: `String` representation of this String object.
func hexadecimalString() -> String? {
return data(using: .utf8)?
.hexadecimal()
}
}
extension Data {
/// Create hexadecimal string representation of `Data` object.
/// - returns: `String` representation of this `Data` object.
func hexadecimal() -> String {
return map { String(format: "%02x", $0) }
.joined(separator: "")
}
}
Use like this :
let hexString = "68656c6c 6f2c2077 6f726c64"
print(String(hexadecimalString: hexString))
Or
let originalString = "hello, world"
print(originalString.hexadecimalString())
After lot searching and thinking here is how you do it
func toByteArray( _ hex:String ) -> [UInt8] {
// remove "-" from Hexadecimal
var hexString = hex.removeWord( "-" )
let size = hexString.characters.count / 2
var result:[UInt8] = [UInt8]( repeating: 0, count: size ) // array with length = size
// for ( int i = 0; i < hexString.length; i += 2 )
for i in stride( from: 0, to: hexString.characters.count, by: 2 ) {
let subHexStr = hexString.subString( i, length: 2 )
result[ i / 2 ] = UInt8( subHexStr, radix: 16 )! // ! - because could be null
}
return result
}
extension String {
func subString( _ from: Int, length: Int ) -> String {
let size = self.characters.count
let to = length + from
if from < 0 || to > size {
return ""
}
var result = ""
for ( idx, char ) in self.characters.enumerated() {
if idx >= from && idx < to {
result.append( char )
}
}
return result
}
func removeWord( _ word:String ) -> String {
var result = ""
let textCharArr = Array( self.characters )
let wordCharArr = Array( word.characters )
var possibleMatch = ""
var i = 0, j = 0
while i < textCharArr.count {
if textCharArr[ i ] == wordCharArr[ j ] {
if j == wordCharArr.count - 1 {
possibleMatch = ""
j = 0
}
else {
possibleMatch.append( textCharArr[ i ] )
j += 1
}
}
else {
result.append( possibleMatch )
possibleMatch = ""
if j == 0 {
result.append( textCharArr[ i ] )
}
else {
j = 0
i -= 1
}
}
i += 1
}
return result
}
}
Refer this video to know how I did it.
Credit : AllTech
Conversion of String to Data with nicer syntax.
static func hexStringToData(string: String) -> Data {
let stringArray = Array(string)
var data: Data = Data()
for i in stride(from: 0, to: string.count, by: 2) {
let pair: String = String(stringArray[i]) + String(stringArray[i+1])
if let byteNum = UInt8(pair, radix: 16) {
let byte = Data([byteNum])
data.append(byte)
}
else{
fatalError()
}
}
return data
}