Skip to content

Commit

Permalink
fix(pollux): ATL-3900 Force use of Bouncycastle (#463)
Browse files Browse the repository at this point in the history
* fix(pollux): ATL-3900 Force use of Bouncycastle

* updated the mercury version

---------

Co-authored-by: Shailesh Patil <shailesh.patil@iohk.io>
  • Loading branch information
CryptoKnightIOG and mineme0110 committed Mar 22, 2023
1 parent 8444057 commit 5b4aa5d
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 7 deletions.
2 changes: 1 addition & 1 deletion pollux/lib/project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ object Dependencies {
val prismSdk = "v1.4.1" // scala-steward:off
val iris = "0.1.0"
val shared = "0.2.0"
val mercury = "0.21.0"
val mercury = "0.22.0"
val castor = "0.8.1"
val flyway = "9.8.3"
val testContainersScalaPostgresql = "0.40.11"
Expand Down
4 changes: 3 additions & 1 deletion pollux/lib/project/Dependencies_VC_JWT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@ object Dependencies_VC_JWT {
private lazy val zioTestSbt = "dev.zio" %% "zio-test-sbt" % Versions.zio % Test
private lazy val zioTestMagnolia = "dev.zio" %% "zio-test-magnolia" % Versions.zio % Test

private lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.2.9" % Test

private lazy val castorCore = "io.iohk.atala" %% "castor-core" % Versions.castor

// Dependency Modules
private lazy val zioDependencies: Seq[ModuleID] = Seq(zio, zioPrelude, zioTest, zioTestSbt, zioTestMagnolia)
private lazy val circeDependencies: Seq[ModuleID] = Seq(coreJwtCirce, genericJwtCirce, parserJwtCirce)
private lazy val baseDependencies: Seq[ModuleID] =
circeDependencies ++ zioDependencies :+ jwtCirce :+ circeJsonSchema :+ nimbusJoseJwt :+ castorCore
circeDependencies ++ zioDependencies :+ jwtCirce :+ circeJsonSchema :+ nimbusJoseJwt :+ castorCore :+ scalaTest

// Project Dependencies
lazy val polluxVcJwtDependencies: Seq[ModuleID] = baseDependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.iohk.atala.pollux.vc.jwt

import com.nimbusds.jose.{JWSAlgorithm, JWSHeader}
import com.nimbusds.jose.crypto.ECDSASigner
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton
import com.nimbusds.jose.jwk.{Curve, ECKey}
import com.nimbusds.jwt.{JWTClaimsSet, SignedJWT}
import io.circe
Expand Down Expand Up @@ -72,9 +73,14 @@ class ES256Signer(privateKey: PrivateKey) extends Signer {
// works with java 7, 8, 11 & bouncycastle provider
// https://connect2id.com/products/nimbus-jose-jwt/jca-algorithm-support#alg-support-table
class ES256KSigner(privateKey: PrivateKey) extends Signer {
lazy val signer: ECDSASigner = {
val ecdsaSigner = ECDSASigner(privateKey, Curve.SECP256K1)
val bouncyCastleProvider = BouncyCastleProviderSingleton.getInstance
ecdsaSigner.getJCAContext.setProvider(bouncyCastleProvider)
ecdsaSigner
}
override def encode(claim: Json): JWT = {
val claimSet = JWTClaimsSet.parse(claim.noSpaces)
val signer = ECDSASigner(privateKey, Curve.SECP256K1)
val signedJwt = SignedJWT(
new JWSHeader.Builder(JWSAlgorithm.ES256K).build(),
claimSet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.nimbusds.jose.jwk.*
import com.nimbusds.jose.jwk.gen.*
import com.nimbusds.jose.util.Base64URL
import com.nimbusds.jose.JWSVerifier
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton
import com.nimbusds.jwt.SignedJWT
import io.circe
import io.circe.generic.auto.*
Expand Down Expand Up @@ -80,14 +81,20 @@ object JWTVerification {
})
}

def toECDSAVerifier(publicKey: PublicKey): JWSVerifier = {
val verifier: JWSVerifier = publicKey match {
case key: ECPublicKey => ECDSAVerifier(key)
case key => throw Exception(s"unsupported public-key type: ${key.getClass.getCanonicalName}")
}
verifier.getJCAContext.setProvider(BouncyCastleProviderSingleton.getInstance)
verifier
}

def validateEncodedJwt(jwt: JWT, publicKey: PublicKey): Validation[String, Unit] = {
Try {
val parsedJwt = SignedJWT.parse(jwt.value)
// TODO Implement other key types
val verifier: JWSVerifier = publicKey match {
case key: ECPublicKey => ECDSAVerifier(key)
case key => throw Exception(s"unsupported public-key type: ${key.getClass.getCanonicalName}")
}
val verifier = toECDSAVerifier(publicKey)
parsedJwt.verify(verifier)
} match {
case Failure(exception) => Validation.fail(s"Jwt[$jwt] verification pre-process failed: ${exception.getMessage}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.iohk.atala.pollux.vc.jwt

import com.nimbusds.jose.crypto.ECDSASigner
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.gen.ECKeyGenerator
import io.circe.Json
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.must.Matchers.mustBe
import org.scalatest.matchers.should.Matchers

import java.security.PrivateKey
import java.security.interfaces.ECPrivateKey

class ECDSAVerifierTest extends AnyFunSuite with Matchers {

test("toECDSAVerifier should use BouncyCastleProviderSingleton") {
val ecKey = ECKeyGenerator(Curve.SECP256K1).generate()
val verifier = JWTVerification.toECDSAVerifier(ecKey.toPublicKey)
val provider = verifier.getJCAContext.getProvider
provider mustBe a[BouncyCastleProvider]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.iohk.atala.pollux.vc.jwt

import com.nimbusds.jose.crypto.ECDSASigner
import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton
import com.nimbusds.jose.jwk.Curve
import com.nimbusds.jose.jwk.gen.ECKeyGenerator
import io.circe.Json
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.must.Matchers.mustBe
import org.scalatest.matchers.should.Matchers

import java.security.PrivateKey
import java.security.interfaces.ECPrivateKey

class ES256KSignerTest extends AnyFunSuite with Matchers {

test("ES256KSigner should use BouncyCastleProviderSingleton") {
val ecKey = ECKeyGenerator(Curve.SECP256K1).generate()
val signer = new ES256KSigner(ecKey.toPrivateKey).signer
val provider = signer.getJCAContext.getProvider
provider mustBe a[BouncyCastleProvider]
}
}

0 comments on commit 5b4aa5d

Please sign in to comment.