• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperledger / identus-cloud-agent / 9106606089

16 May 2024 04:43AM UTC coverage: 52.923% (+1.2%) from 51.74%
9106606089

push

web-flow
test: add tests for the new key types (#1044)

Signed-off-by: Pat Losoponkul <pat.losoponkul@iohk.io>

34 of 46 new or added lines in 7 files covered. (73.91%)

322 existing lines in 66 files now uncovered.

7531 of 14230 relevant lines covered (52.92%)

0.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.55
/pollux/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/revocation/BitString.scala
1
package org.hyperledger.identus.pollux.vc.jwt.revocation
2

3
import org.hyperledger.identus.pollux.vc.jwt.revocation.BitStringError.{DecodingError, EncodingError, IndexOutOfBounds}
4
import zio.{IO, UIO, ZIO}
5

6
import java.io.{ByteArrayInputStream, ByteArrayOutputStream}
7
import java.util
8
import java.util.Base64
9
import java.util.zip.{GZIPInputStream, GZIPOutputStream}
10

11
class BitString private (val bitSet: util.BitSet, val size: Int) {
12
  def setRevokedInPlace(index: Int, value: Boolean): IO[IndexOutOfBounds, Unit] =
1✔
13
    if (index >= size) ZIO.fail(IndexOutOfBounds(s"bitIndex >= $size: $index"))
1✔
14
    else ZIO.attempt(bitSet.set(index, value)).mapError(t => IndexOutOfBounds(t.getMessage))
1✔
15

16
  def isRevoked(index: Int): IO[IndexOutOfBounds, Boolean] =
1✔
17
    if (index >= size) ZIO.fail(IndexOutOfBounds(s"bitIndex >= $size: $index"))
1✔
18
    else ZIO.attempt(bitSet.get(index)).mapError(t => IndexOutOfBounds(t.getMessage))
1✔
19

20
  def revokedCount(): UIO[Int] = ZIO.succeed(bitSet.stream().count().toInt)
1✔
21

22
  def encoded: IO[EncodingError, String] = {
1✔
23
    for {
1✔
24
      bitSetByteArray <- ZIO.succeed(bitSet.toByteArray)
1✔
25
      /*
26
      This is where the size constructor parameter comes into play (i.e. the initial bitstring size requested by the user).
27
      Interestingly, the underlying 'bitSet.toByteArray()' method only returns the byte array that are 'in use', which means the bytes needed to hold the current bits that are set to true.
28
      E.g. Calling toByteArray on a BitSet of size 64, where all bits are false, will return an empty array. The same BitSet with the fourth bit set to true will return 1 byte. And so on...
29
      So, the paddingByteArray is used to fill the gap between what BitSet returns and what was requested by the user.
30
      If the BitString size is 131.072 and no VC is revoked, the final encoding (as per the spec) should account for all bits, and no only those that are revoked.
31
      The (x + 7) / 8) is used to calculate the number of bytes needed to store a bit array of size x.
32
       */
33
      paddingByteArray = new Array[Byte](((size + 7) / 8) - bitSetByteArray.length)
1✔
34
      baos = new ByteArrayOutputStream()
1✔
35
      _ <- (for {
1✔
36
        gzipOutputStream <- ZIO.attempt(new GZIPOutputStream(baos))
1✔
37
        _ <- ZIO.attempt(gzipOutputStream.write(bitSetByteArray))
1✔
38
        _ <- ZIO.attempt(gzipOutputStream.write(paddingByteArray))
1✔
39
        _ <- ZIO.attempt(gzipOutputStream.close())
1✔
UNCOV
40
      } yield ()).mapError(t => EncodingError(t.getMessage))
×
41
    } yield {
1✔
42
      Base64.getUrlEncoder.encodeToString(baos.toByteArray)
1✔
43
    }
44
  }
45
}
46

47
object BitString {
48
  /*
49
   The minimum size of the bit string according to the VC Status List 2021 specification.
50
   As per the spec "... a minimum revocation bitstring of 131.072, or 16KB uncompressed... is enough to give holders an adequate amount of herd privacy"
51
   Cf. https://www.w3.org/TR/vc-status-list/#revocation-bitstring-length
52
   */
53
  val MIN_SL2021_SIZE: Int = 131072
54

55
  def getInstance(): IO[BitStringError, BitString] = getInstance(MIN_SL2021_SIZE)
1✔
56

57
  def getInstance(size: Int): IO[BitStringError, BitString] = {
1✔
58
    if (size % 8 != 0) ZIO.fail(BitStringError.InvalidSize("Bit string size should be a multiple of 8"))
1✔
59
    else ZIO.succeed(BitString(new util.BitSet(size), size))
1✔
60
  }
61

62
  def valueOf(b64Value: String): IO[DecodingError, BitString] = {
1✔
63
    for {
1✔
UNCOV
64
      ba <- ZIO.attempt(Base64.getUrlDecoder.decode(b64Value)).mapError(t => DecodingError(t.getMessage))
×
65
    } yield {
66
      val bais = new ByteArrayInputStream(ba)
1✔
67
      val gzipInputStream = new GZIPInputStream(bais)
1✔
68
      val byteArray = gzipInputStream.readAllBytes()
1✔
69
      BitString(util.BitSet.valueOf(byteArray), byteArray.length * 8)
1✔
70
    }
71
  }
72
}
73

74
sealed trait BitStringError
75

76
object BitStringError {
77
  final case class InvalidSize(message: String) extends BitStringError
78
  final case class EncodingError(message: String) extends BitStringError
79
  final case class DecodingError(message: String) extends BitStringError
80
  final case class IndexOutOfBounds(message: String) extends BitStringError
81
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc