Skip to content

Commit

Permalink
Merge pull request #38 from ravikp/byte-representation-of-data
Browse files Browse the repository at this point in the history
Implement consistent network ordered byte representation on Android and IOS for
- Response size (4 bytes)
- Seq number & Checksum for chunks (2 bytes)
- missing chunk sequence number & Page size in transfer report (2 bytes)
  • Loading branch information
krishnakumar4a4 authored Apr 11, 2023
2 parents 7d03b28 + 921b92b commit ab0caad
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 62 deletions.
7 changes: 4 additions & 3 deletions android/src/main/java/io/mosip/tuvali/transfer/Assembler.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.mosip.tuvali.transfer

import android.util.Log
import io.mosip.tuvali.transfer.Util.Companion.twoBytesToIntBigEndian
import io.mosip.tuvali.transfer.ByteCount.TwoBytes
import io.mosip.tuvali.transfer.Util.Companion.networkOrderedByteArrayToInt
import io.mosip.tuvali.verifier.exception.CorruptedChunkReceivedException
import io.mosip.tuvali.transfer.Util.Companion.getLogTag

Expand All @@ -26,8 +27,8 @@ class Assembler(private val totalSize: Int, private val maxDataBytes: Int ): Chu
Log.e(logTag, "received invalid chunk chunkSize: ${chunkData.size}, lastReadSeqNumber: $lastReadSeqNumber")
return 0
}
val seqNumberInMeta = twoBytesToIntBigEndian(chunkData.copyOfRange(0, 2))
val crcReceived = twoBytesToIntBigEndian(chunkData.copyOfRange(2,4)).toUShort()
val seqNumberInMeta = networkOrderedByteArrayToInt(chunkData.copyOfRange(0, 2), TwoBytes)
val crcReceived = networkOrderedByteArrayToInt(chunkData.copyOfRange(2,4), TwoBytes).toUShort()

//Log.d(logTag, "received add chunk received chunkSize: ${chunkData.size}, seqNumberInMeta: $seqNumberInMeta")

Expand Down
7 changes: 5 additions & 2 deletions android/src/main/java/io/mosip/tuvali/transfer/Chunker.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package io.mosip.tuvali.transfer

import android.util.Log
import io.mosip.tuvali.transfer.Util.Companion.intToTwoBytesBigEndian
import io.mosip.tuvali.transfer.ByteCount.TwoBytes
import io.mosip.tuvali.transfer.Util.Companion.intToNetworkOrderedByteArray
import io.mosip.tuvali.transfer.Util.Companion.getLogTag

class Chunker(private val data: ByteArray, private val maxDataBytes: Int) :
Expand Down Expand Up @@ -57,7 +58,9 @@ class Chunker(private val data: ByteArray, private val maxDataBytes: Int) :
val dataChunk = data.copyOfRange(fromIndex, toIndex)
val crc = CheckValue.get(dataChunk)

return intToTwoBytesBigEndian(seqNumber) + intToTwoBytesBigEndian(crc.toInt()) + dataChunk

return intToNetworkOrderedByteArray(seqNumber, TwoBytes) + intToNetworkOrderedByteArray(crc.toInt(), TwoBytes) + dataChunk

}

fun isComplete(): Boolean {
Expand Down
10 changes: 6 additions & 4 deletions android/src/main/java/io/mosip/tuvali/transfer/TransferReport.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.mosip.tuvali.transfer

import android.util.Log
import io.mosip.tuvali.transfer.ByteCount.TwoBytes
import io.mosip.tuvali.transfer.Util.Companion.networkOrderedByteArrayToInt
import kotlin.math.ceil
import kotlin.math.min

Expand Down Expand Up @@ -31,15 +33,15 @@ class TransferReport {

constructor(bytes: ByteArray){
this.type = ReportType.values()[bytes[0].toInt()]
this.totalPages = Util.twoBytesToIntBigEndian(byteArrayOf(bytes[1], bytes[2]))
this.totalPages = networkOrderedByteArrayToInt(byteArrayOf(bytes[1], bytes[2]), TwoBytes)
val sequenceNumberByteArray = bytes.drop(PAGE_NUMBER_SIZE_IN_BYTES + TYPE_SIZE_IN_BYTES)
this.missingSequences = sequenceNumberByteArray.chunked(2)
.fold(intArrayOf())
{ acc, twoBytes ->
if (twoBytes.size != 2) {
acc
} else {
acc + Util.twoBytesToIntBigEndian(twoBytes.toByteArray())
acc + networkOrderedByteArrayToInt(twoBytes.toByteArray(),TwoBytes)
}
}
}
Expand All @@ -52,8 +54,8 @@ class TransferReport {
+---------+------------------+---------------------+-------------------+-------------------+-------------------+
*/
fun toByteArray(): ByteArray {
val missingSeqBytes = missingSequences?.fold(byteArrayOf()) { acc, sNo -> acc + Util.intToTwoBytesBigEndian(sNo) }
val metadata = byteArrayOf(type.ordinal.toByte()) + Util.intToTwoBytesBigEndian(totalPages)
val missingSeqBytes = missingSequences?.fold(byteArrayOf()) { acc, sNo -> acc + Util.intToNetworkOrderedByteArray(sNo, TwoBytes) }
val metadata = byteArrayOf(type.ordinal.toByte()) + Util.intToNetworkOrderedByteArray(totalPages, TwoBytes)

return if(missingSeqBytes != null) {
metadata + missingSeqBytes
Expand Down
58 changes: 37 additions & 21 deletions android/src/main/java/io/mosip/tuvali/transfer/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import java.security.MessageDigest
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import io.mosip.tuvali.openid4vpble.Openid4vpBleModule
import io.mosip.tuvali.transfer.ByteCount.FourBytes
import io.mosip.tuvali.transfer.ByteCount.TwoBytes
import java.nio.ByteBuffer


enum class ByteCount(val size: Int) {
FourBytes(4),
TwoBytes(2)
}
class Util {
companion object {
fun getSha256(data: ByteArray): String {
Expand All @@ -19,16 +25,6 @@ class Util {
return Hex.toHexString(md.digest())
}

// Big Endian conversion:
// Convert int to an array with 2 bytes
fun intToTwoBytesBigEndian(num: Int): ByteArray {
if (num < 256) {
val minValue = 0
return byteArrayOf(minValue.toByte(), num.toByte())
}
return byteArrayOf((num/256).toByte(), (num%256).toByte())
}

// Big endian conversion: If there are two bytes in an array. The first byte of array will treated as MSB
// and second byte is treated as LSB
/*
Expand All @@ -37,19 +33,40 @@ class Util {
| firstByte | secondByte |
| (byte 0) | (byte 1) |
+------------+-------------+
secondByte.toInt() + (256 * firstByte.toInt())
*/

@OptIn(ExperimentalUnsignedTypes::class)
fun twoBytesToIntBigEndian(num: ByteArray): Int {
val convertedNum = num.toUByteArray()
val firstByte: UByte = convertedNum[0]
val secondByte: UByte = convertedNum[1]
return secondByte.toInt() + (256 * firstByte.toInt())
fun intToNetworkOrderedByteArray(num: Int, byteCount: ByteCount): ByteArray {
val byteBuffer = ByteBuffer.allocate(byteCount.size)
val byteArray = when (byteCount) {
FourBytes -> {
byteBuffer.putInt(num)
byteBuffer.array()
}
TwoBytes -> {
byteBuffer.putShort(num.toShort())
byteBuffer.array()
}
}
byteBuffer.clear()
return byteArray

}

fun networkOrderedByteArrayToInt(num: ByteArray, byteCount: ByteCount): Int{
val byteBuffer = ByteBuffer.wrap(num)
var intValue = when (byteCount){
FourBytes -> {
byteBuffer.int
}
TwoBytes -> {
byteBuffer.short.toInt()
}
}
byteBuffer.clear()
return intValue
}

fun compress(bytes: ByteArray): ByteArray? {
// return bytes
val out = ByteArrayOutputStream()
try {
GZIPOutputStream(out).use { gzipOutputStream ->
Expand All @@ -63,7 +80,6 @@ class Util {
}

fun decompress(bytes: ByteArray): ByteArray? {
// return bytes
try {
GZIPInputStream(ByteArrayInputStream(bytes)).use { gzipInputStream -> return gzipInputStream.readBytes() }
} catch (e: Exception) {
Expand All @@ -78,7 +94,7 @@ class Util {
}

fun getLogTag(moduleName: String): String{
return "${moduleName} : v${Openid4vpBleModule.tuvaliVersion}"
return "$moduleName : v${Openid4vpBleModule.tuvaliVersion}"
}
}
}
4 changes: 2 additions & 2 deletions android/src/main/java/io/mosip/tuvali/verifier/Verifier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.mosip.tuvali.cryptography.SecretsTranslator
import io.mosip.tuvali.cryptography.VerifierCryptoBox
import io.mosip.tuvali.cryptography.VerifierCryptoBoxBuilder
import io.mosip.tuvali.openid4vpble.Openid4vpBleModule
import io.mosip.tuvali.transfer.ByteCount.FourBytes
import io.mosip.tuvali.transfer.TransferReportRequest
import io.mosip.tuvali.transfer.Util
import io.mosip.tuvali.transfer.Util.Companion.getLogTag
Expand Down Expand Up @@ -168,8 +169,7 @@ class Verifier(
}
GattService.RESPONSE_SIZE_CHAR_UUID -> {
value?.let {
//Log.d(logTag, "received response size on characteristic value: ${String(value)}")
val responseSize: Int = String(value).toInt()
val responseSize: Int = Util.networkOrderedByteArrayToInt(value, FourBytes)
Log.d(logTag, "received response size on characteristic: $responseSize")
val responseSizeReadSuccessMessage = ResponseSizeReadSuccessMessage(responseSize, maxDataBytes)
transferHandler.sendMessage(responseSizeReadSuccessMessage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@ import android.os.Message
import android.util.Log
import io.mosip.tuvali.ble.peripheral.Peripheral
import io.mosip.tuvali.openid4vpble.exception.exception.TransferHandlerException
import io.mosip.tuvali.transfer.Assembler
import io.mosip.tuvali.transfer.TransferReportRequest
import io.mosip.tuvali.transfer.TransferReport
import io.mosip.tuvali.transfer.*
import io.mosip.tuvali.verifier.GattService
import io.mosip.tuvali.verifier.exception.CorruptedChunkReceivedException
import io.mosip.tuvali.verifier.exception.TooManyFailureChunksException
import io.mosip.tuvali.verifier.transfer.message.*
import java.util.*
import kotlin.math.ceil
import io.mosip.tuvali.transfer.Util.Companion.getLogTag

class TransferHandler(looper: Looper, private val peripheral: Peripheral, private val transferListener: ITransferListener, val serviceUUID: UUID) : Handler(looper) {
Expand Down Expand Up @@ -123,7 +120,7 @@ class TransferHandler(looper: Looper, private val peripheral: Peripheral, privat
if (assembler?.isComplete() == true) {
return
}
//Log.d(logTag, "SequenceNumber: ${Util.twoBytesToIntBigEndian(chunkData.copyOfRange(0,2))}, Sha256: ${Util.getSha256(chunkData)}")
//Log.d(logTag, "SequenceNumber: ${Util.byteArrayToInt(chunkData.copyOfRange(0,2), TwoBytes)}, Sha256: ${Util.getSha256(chunkData)}")
assembler?.addChunk(chunkData)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.util.Log
import io.mosip.tuvali.ble.central.Central
import io.mosip.tuvali.openid4vpble.exception.exception.TransferHandlerException
import io.mosip.tuvali.transfer.*
import io.mosip.tuvali.transfer.ByteCount.FourBytes
import io.mosip.tuvali.verifier.GattService
import io.mosip.tuvali.wallet.transfer.message.*
import java.util.*
Expand Down Expand Up @@ -191,7 +192,7 @@ class TransferHandler(looper: Looper, private val central: Central, val serviceU
central.write(
serviceUUID,
GattService.RESPONSE_SIZE_CHAR_UUID,
"$size".toByteArray()
Util.intToNetworkOrderedByteArray(size, FourBytes)
)
}

Expand Down
8 changes: 4 additions & 4 deletions ios/Openid4vpBle.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/* Begin PBXBuildFile section */
0A62BB712938C1EC0092E47D /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A62BB702938C1EC0092E47D /* Wallet.swift */; };
0A62BB732938C1FB0092E47D /* Verifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A62BB722938C1FA0092E47D /* Verifier.swift */; };
0ACACE8A2966D8C800DABDFE /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ACACE892966D8C800DABDFE /* Utils.swift */; };
0ACACE8A2966D8C800DABDFE /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ACACE892966D8C800DABDFE /* Util.swift */; };
0D36889829A782AA000B12CD /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D36889629A782AA000B12CD /* ErrorHandler.swift */; };
0D36889929A782AA000B12CD /* OpenId4vpError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D36889729A782AA000B12CD /* OpenId4vpError.swift */; };
8D069F652964466900AB61A9 /* Central.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D069F642964466900AB61A9 /* Central.swift */; };
Expand Down Expand Up @@ -58,7 +58,7 @@
/* Begin PBXFileReference section */
0A62BB702938C1EC0092E47D /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = "<group>"; };
0A62BB722938C1FA0092E47D /* Verifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Verifier.swift; sourceTree = "<group>"; };
0ACACE892966D8C800DABDFE /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
0ACACE892966D8C800DABDFE /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = "<group>"; };
0D36889629A782AA000B12CD /* ErrorHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
0D36889729A782AA000B12CD /* OpenId4vpError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenId4vpError.swift; sourceTree = "<group>"; };
134814201AA4EA6300B7C361 /* libOpenid4vpBle.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libOpenid4vpBle.a; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -194,7 +194,7 @@
8D069F68296447D000AB61A9 /* Peripheral.swift */,
8D069F6A29644B7C00AB61A9 /* PeripheralExtension.swift */,
8D069F6C2964724700AB61A9 /* Characteristics.swift */,
0ACACE892966D8C800DABDFE /* Utils.swift */,
0ACACE892966D8C800DABDFE /* Util.swift */,
E2D682D329A494D8009E71A6 /* PeripheralCommunicatorDelegate.swift */,
);
path = peripheral;
Expand Down Expand Up @@ -365,7 +365,7 @@
8D179C892954871400017EE5 /* CipherBox.swift in Sources */,
8D31C3F62956AF460073B710 /* CryptoConstants.swift in Sources */,
8D179C9129548B9A00017EE5 /* CipherPackage.swift in Sources */,
0ACACE8A2966D8C800DABDFE /* Utils.swift in Sources */,
0ACACE8A2966D8C800DABDFE /* Util.swift in Sources */,
F4FF95D7245B92E800C19C63 /* Openid4vpBle.swift in Sources */,
E2F1AFCB297176C200AC3355 /* BLEConstants.swift in Sources */,
8D179C8B2954874C00017EE5 /* CiperBoxImpl.swift in Sources */,
Expand Down
13 changes: 7 additions & 6 deletions ios/ble/Utility/TransferHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class TransferHandler {
private var failureFrameRetryCounter = 0
private let MAX_FAILURE_FRAME_RETRY_LIMIT = 15


func initialize(initdData: Data) {
data = initdData
}
Expand All @@ -34,7 +35,7 @@ class TransferHandler {
sendResponseSize(size: msg.dataSize!)
}
else if msg.msgType == .RESPONSE_SIZE_WRITE_SUCCESS {
responseStartTimeInMillis = Utils.currentTimeInMilliSeconds()
responseStartTimeInMillis = Util.currentTimeInMilliSeconds()
currentState = States.ResponseSizeWriteSuccess
initResponseChunkSend()
} else if msg.msgType == .RESPONSE_SIZE_WRITE_FAILED {
Expand Down Expand Up @@ -79,7 +80,8 @@ class TransferHandler {

private func requestTransmissionReport() {
var notifyObj: Data
delegate?.write(serviceUuid: BLEConstants.SERVICE_UUID, charUUID: NetworkCharNums.TRANSFER_REPORT_REQUEST_CHAR_UUID, data: withUnsafeBytes(of: 1.littleEndian) { Data($0) }, withResponse: true)
let data = Data([UInt8(clamping: 1)])
delegate?.write(serviceUuid: BLEConstants.SERVICE_UUID, charUUID: NetworkCharNums.TRANSFER_REPORT_REQUEST_CHAR_UUID, data: data, withResponse: true)
os_log(.info, "transmission report requested")
}

Expand Down Expand Up @@ -109,10 +111,9 @@ class TransferHandler {
}

private func sendResponseSize(size: Int) {
let decimalString = String(size)
if let data = decimalString.data(using: .utf8) {
delegate?.write(serviceUuid: Peripheral.SERVICE_UUID, charUUID: NetworkCharNums.RESPONSE_SIZE_CHAR_UUID, data: data, withResponse: true)
}
let sizeByteArray = Util.intToNetworkOrderedByteArray(num: size, byteCount: Util.ByteCount.FourBytes)
delegate?.write(serviceUuid: Peripheral.SERVICE_UUID, charUUID: NetworkCharNums.RESPONSE_SIZE_CHAR_UUID, data: sizeByteArray, withResponse: true)

}

private func initResponseChunkSend() {
Expand Down
4 changes: 2 additions & 2 deletions ios/ble/Utility/TransferReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ class TransferReport {
init(bytes: Data) {
if bytes.count >= 3 {
type = ReportType(rawValue: Int(bytes[0]))!
totalPages = Utils.twoBytesToIntBigEndian(num: bytes.subdata(in: (bytes.startIndex+1..<bytes.startIndex+3)))
totalPages = Util.networkOrderedByteArrayToInt(num: bytes.subdata(in: (bytes.startIndex+1..<bytes.startIndex+3)))
var missingChunksData = bytes.dropFirst(3)
if missingChunksData.count > 0 {
missingSequences = []
while missingChunksData.count >= 2 {
let missingChunkSlice = missingChunksData.subdata(in: missingChunksData.startIndex..<missingChunksData.startIndex+2)
missingSequences?.append(Utils.twoBytesToIntBigEndian(num: missingChunkSlice))
missingSequences?.append(Util.networkOrderedByteArrayToInt(num: missingChunkSlice))
missingChunksData = missingChunksData.dropFirst(2)
}
//os_log(.debug,"%{public}@", missingSequences)
Expand Down
6 changes: 1 addition & 5 deletions ios/ble/Utility/chunker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class Chunker {
if let chunkData = chunkData {
let payload = chunkData.subdata(in: fromIndex + chunkData.startIndex..<chunkData.startIndex + toIndex)
let payloadCRC = CRC.evaluate(d: payload)
return intToBytes(UInt16(seqNumber)) + intToBytes(payloadCRC) + payload
return Util.intToNetworkOrderedByteArray(num: seqNumber, byteCount: Util.ByteCount.TwoBytes) + Util.intToNetworkOrderedByteArray(num: Int(payloadCRC), byteCount: Util.ByteCount.TwoBytes) + payload
}
return Data() //
}
Expand All @@ -105,8 +105,4 @@ class Chunker {
return isComplete
}

func intToBytes(_ value: UInt16) -> Data {
var value = value.bigEndian
return Data(bytes: &value, count: MemoryLayout<UInt16>.size)
}
}
2 changes: 1 addition & 1 deletion ios/ble/peripheral/Peripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Peripheral: NSObject {

func setupPeripheralsAndStartAdvertising() {
let bleService = CBMutableService(type: Self.SERVICE_UUID, primary: true)
bleService.characteristics = Utils.createCBMutableCharacteristics()
bleService.characteristics = Util.createCBMutableCharacteristics()
peripheralManager.add(bleService)
peripheralManager.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [Self.SERVICE_UUID, Self.SCAN_RESPONSE_SERVICE_UUID], CBAdvertisementDataLocalNameKey: "verifier"])
}
Expand Down
Loading

0 comments on commit ab0caad

Please sign in to comment.