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

input-output-hk / atala-prism-building-blocks / 8819221863

24 Apr 2024 03:32PM UTC coverage: 50.082% (+18.2%) from 31.912%
8819221863

Pull #973

mineme0110
minor cleanup

Signed-off-by: mineme0110 <shailesh.patil@iohk.io>
Pull Request #973: feat: Align the repo with new name identus-cloud-agent

1 of 6 new or added lines in 4 files covered. (16.67%)

2998 existing lines in 264 files now uncovered.

7340 of 14656 relevant lines covered (50.08%)

0.5 hits per line

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

96.05
/pollux/lib/vc-jwt/src/main/scala/io/iohk/atala/pollux/vc/jwt/Proof.scala
1
package io.iohk.atala.pollux.vc.jwt
2

3
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton
4
import io.circe.*
5
import io.circe.syntax.*
6
import cats.implicits.*
7
import java.time.{Instant, ZoneOffset}
8
import zio.*
9
import io.iohk.atala.shared.utils.Json as JsonUtils
10
import io.iohk.atala.shared.utils.Base64Utils
11
import scodec.bits.ByteVector
12
import scala.util.Try
13
import java.security.*
14
import java.security.spec.X509EncodedKeySpec
15

16
sealed trait Proof {
17
  val id: Option[String] = None
18
  val `type`: String
19
  val proofPurpose: String
20
  val verificationMethod: String
21
  val created: Option[Instant] = None
22
  val domain: Option[String] = None
23
  val challenge: Option[String] = None
24
  val proofValue: String
25
  val previousProof: Option[String] = None
26
  val nonce: Option[String] = None
27
}
28

29
object Proof {
30
  given decodeProof: Decoder[Proof] = new Decoder[Proof] {
31
    final def apply(c: HCursor): Decoder.Result[Proof] = {
1✔
32
      val decoders: List[Decoder[Proof]] = List(
1✔
33
        Decoder[EddsaJcs2022Proof].widen
1✔
34
          // Note: Add another proof types here when available
35
      )
36

37
      decoders.foldLeft(
38
        Left[DecodingFailure, Proof](DecodingFailure("Cannot decode as Proof", c.history)): Decoder.Result[Proof]
1✔
39
      ) { (acc, decoder) =>
1✔
40
        acc.orElse(decoder.tryDecode(c))
1✔
41
      }
42
    }
43
  }
44
}
45

46
object EddsaJcs2022ProofGenerator {
47
  private val provider = BouncyCastleProviderSingleton.getInstance
1✔
48
  def generateProof(payload: Json, sk: PrivateKey, pk: PublicKey): Task[EddsaJcs2022Proof] = {
1✔
49
    for {
1✔
50
      canonicalizedJsonString <- ZIO.fromEither(JsonUtils.canonicalizeToJcs(payload.spaces2))
1✔
51
      canonicalizedJson <- ZIO.fromEither(parser.parse(canonicalizedJsonString))
1✔
52
      dataToSign = canonicalizedJson.noSpaces.getBytes
1✔
53
      signature = sign(sk, dataToSign)
1✔
54
      base58BtsEncodedSignature = MultiBaseString(
55
        header = MultiBaseString.Header.Base58Btc,
56
        data = ByteVector.view(signature).toBase58
1✔
57
      ).toMultiBaseString
1✔
58
      created = Instant.now()
1✔
59
      multiKey = MultiKey(publicKeyMultibase =
1✔
60
        Some(MultiBaseString(header = MultiBaseString.Header.Base64Url, data = Base64Utils.encodeURL(pk.getEncoded)))
1✔
61
      )
62
      verificationMethod = Base64Utils.createDataUrl(
1✔
63
        multiKey.asJson.dropNullValues.noSpaces.getBytes,
1✔
64
        "application/json"
65
      )
66
    } yield EddsaJcs2022Proof(
1✔
67
      proofValue = base58BtsEncodedSignature,
68
      maybeCreated = Some(created),
69
      verificationMethod = verificationMethod
70
    )
71
  }
72

73
  def verifyProof(payload: Json, proofValue: String, pk: MultiKey): Task[Boolean] = {
1✔
74

75
    val res = for {
1✔
76
      canonicalizedJsonString <- ZIO
1✔
77
        .fromEither(JsonUtils.canonicalizeToJcs(payload.spaces2))
1✔
78
        .mapError(_.getMessage)
×
79
      canonicalizedJson <- ZIO
1✔
80
        .fromEither(parser.parse(canonicalizedJsonString))
1✔
81
        .mapError(_.getMessage)
×
82
      dataToVerify = canonicalizedJson.noSpaces.getBytes
1✔
83
      signature <- ZIO.fromEither(MultiBaseString.fromString(proofValue).flatMap(_.getBytes))
1✔
84
      publicKeyBytes <- ZIO.fromEither(
1✔
85
        pk.publicKeyMultibase.toRight("No public key provided inside MultiKey").flatMap(_.getBytes)
1✔
86
      )
87
      javaPublicKey <- ZIO.fromEither(recoverPublicKey(publicKeyBytes))
1✔
88
      isValid = verify(javaPublicKey, signature, dataToVerify)
1✔
89

90
    } yield isValid
1✔
91

UNCOV
92
    res.mapError(e => Throwable(e))
×
93
  }
94

95
  private def sign(privateKey: PrivateKey, data: Array[Byte]): Array[Byte] = {
1✔
96

97
    val signer = Signature.getInstance("SHA256withECDSA", provider)
1✔
98
    signer.initSign(privateKey)
1✔
99
    signer.update(data)
1✔
100
    signer.sign()
1✔
101
  }
102

103
  private def recoverPublicKey(pkBytes: Array[Byte]): Either[String, PublicKey] = {
1✔
104
    val keyFactory = KeyFactory.getInstance("EC", provider)
1✔
105
    val x509KeySpec = X509EncodedKeySpec(pkBytes)
1✔
106
    Try(keyFactory.generatePublic(x509KeySpec)).toEither.left.map(_.getMessage)
1✔
107
  }
108

109
  private def verify(publicKey: PublicKey, signature: Array[Byte], data: Array[Byte]): Boolean = {
1✔
110
    val verifier = Signature.getInstance("SHA256withECDSA", provider)
1✔
111
    verifier.initVerify(publicKey)
1✔
112
    verifier.update(data)
1✔
113
    verifier.verify(signature)
1✔
114
  }
115
}
116
case class EddsaJcs2022Proof(proofValue: String, verificationMethod: String, maybeCreated: Option[Instant])
117
    extends Proof {
118
  override val created: Option[Instant] = maybeCreated
119
  override val `type`: String = "DataIntegrityProof"
120
  override val proofPurpose: String = "assertionMethod"
121
  val cryptoSuite: String = "eddsa-jcs-2022"
122
}
123

124
object EddsaJcs2022Proof {
125

126
  given proofEncoder: Encoder[EddsaJcs2022Proof] =
127
    (proof: EddsaJcs2022Proof) =>
128
      Json
129
        .obj(
1✔
130
          ("id", proof.id.asJson),
1✔
131
          ("type", proof.`type`.asJson),
1✔
132
          ("proofPurpose", proof.proofPurpose.asJson),
1✔
133
          ("verificationMethod", proof.verificationMethod.asJson),
1✔
134
          ("created", proof.created.map(_.atOffset(ZoneOffset.UTC)).asJson),
1✔
135
          ("domain", proof.domain.asJson),
1✔
136
          ("challenge", proof.challenge.asJson),
1✔
137
          ("proofValue", proof.proofValue.asJson),
1✔
138
          ("cryptoSuite", proof.cryptoSuite.asJson),
1✔
139
          ("previousProof", proof.previousProof.asJson),
1✔
140
          ("nonce", proof.nonce.asJson),
1✔
141
          ("cryptoSuite", proof.cryptoSuite.asJson),
1✔
142
        )
143

144
  given proofDecoder: Decoder[EddsaJcs2022Proof] =
145
    (c: HCursor) =>
146
      for {
1✔
147
        id <- c.downField("id").as[Option[String]]
1✔
148
        `type` <- c.downField("type").as[String]
1✔
149
        proofPurpose <- c.downField("proofPurpose").as[String]
1✔
150
        verificationMethod <- c.downField("verificationMethod").as[String]
1✔
151
        created <- c.downField("created").as[Option[Instant]]
1✔
152
        domain <- c.downField("domain").as[Option[String]]
1✔
153
        challenge <- c.downField("challenge").as[Option[String]]
1✔
154
        proofValue <- c.downField("proofValue").as[String]
1✔
155
        previousProof <- c.downField("previousProof").as[Option[String]]
1✔
156
        nonce <- c.downField("nonce").as[Option[String]]
1✔
157
        cryptoSuite <- c.downField("cryptoSuite").as[String]
1✔
158
      } yield {
159
        EddsaJcs2022Proof(
160
          proofValue = proofValue,
161
          verificationMethod = verificationMethod,
162
          maybeCreated = created
163
        )
164
      }
165
}
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