Skip to content

Commit

Permalink
fix: wallet-api encoded list decoding - consider bit order left-to-right
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeplotean committed Feb 12, 2025
1 parent a3df3ae commit c699b2d
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject
import java.io.InputStream
import java.util.Base64
import java.util.BitSet

@OptIn(ExperimentalStdlibApi::class)
object StringUtils {
Expand Down Expand Up @@ -37,30 +36,47 @@ object Base64Utils {

object BitstringUtils {
private val logger = KotlinLogging.logger {}
private const val BITS_PER_BYTE_UNSIGNED = 8u

fun getBitValue(inputStream: InputStream, index: ULong, bitSize: Int): List<Char> =
inputStream.use { stream ->
if (stream.markSupported()) {
stream.mark(Int.MAX_VALUE)
logger.debug { "available bytes: ${bitmap(stream.readAllBytes())}" }
inputStream.reset()
}
//TODO: bitSize constraints
val bitStartPosition = index * bitSize.toUInt()
logger.debug { "bitStartPosition: $bitStartPosition" }
val byteStart = bitStartPosition / 8u
logger.debug { "bitStartPosition overall: $bitStartPosition" }
val byteStart = bitStartPosition / BITS_PER_BYTE_UNSIGNED
logger.debug { "skipping: $byteStart bytes" }
stream.skip(byteStart.toLong())
logger.debug { "available: ${stream.available()} bytes" }
val bytesToRead = (bitSize - 1) / 8 + 1
val bytesToRead = (bitSize - 1) / BITS_PER_BYTE_UNSIGNED.toInt() + 1
logger.debug { "readingNext: $bytesToRead bytes" }
extractBitValue(stream.readNBytes(bytesToRead), index, bitSize.toUInt())
}

private fun extractBitValue(bytes: ByteArray, index: ULong, bitSize: UInt): List<Char> {
val bitSet = BitSet.valueOf(bytes)
logger.debug { "bits set: ${bitSet.length()}" }
val bitStart = index * bitSize % 8u
logger.debug { "startingFromBit: $bitStart" }
val result = mutableListOf<Char>()
for (i in bitStart..<bitStart + bitSize) {
val b = bitSet[i.toInt()].takeIf { it }?.let { 1 } ?: 0
result.add(b.digitToChar())
logger.debug { "selected byte: ${bitmap(bytes)}" }
val bits = bytes.toBitSequence()
val bitStartPosition = index * bitSize % BITS_PER_BYTE_UNSIGNED
logger.debug { "bitStartPosition within byte: $bitStartPosition" }
val bitSet = bits.drop(bitStartPosition.toInt()).iterator()
val result = mutableListOf<Boolean>()
var b = 0u
while (b++ < bitSize) {
result.add(bitSet.next())
}
return result
return result.map { if (it) '1' else '0' }
}

fun ByteArray.toBitSequence(): Sequence<Boolean> = this.fold(emptySequence<Boolean>()) { acc, byte ->
acc + (7 downTo 0).map { i -> (byte.toUInt() shr i) and 1u == 1u }.asSequence()
}

fun bitmap(byteArray: ByteArray) = byteArray.map { byte ->
// Convert byte to binary string
String.format("%8s", Integer.toBinaryString(byte.toInt() and 0xFF)).replace(' ', '0')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import id.walt.webwallet.utils.StringUtils.hexToByteArray
import kotlin.test.*

class BitstringUtilsTest {
private val bitString = hexToByteArray("5AA5")//0b01011010_10100101
private val bitString = hexToByteArray("A265")//0b10100010_01100101

@Test
fun `test unit bitsize, no overflow`() {
Expand Down

0 comments on commit c699b2d

Please sign in to comment.