Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NOT TO MERGE Crypto agility ATL-6707 #835

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import sbtprotoc.ProtocPlugin.autoImport.PB
inThisBuild(
Seq(
organization := "io.iohk.atala",
scalaVersion := "2.13.7",
scalaVersion := "2.13.12", // "2.13.7",
fork := true,
run / connectInput := true,
versionScheme := Some("semver-spec"),
Expand Down Expand Up @@ -50,7 +50,10 @@ lazy val versions = new {
val typesafeConfig = "1.4.2"
val fs2 = "3.2.5"
val scalaUri = "4.0.0"
val prismSdk = "v1.4.1-snapshot-1688975371-7541fd2" // deployed to github packages from sdk branch "node-1.4-extension-sdk"
val prismSdk = "v1.4.1-snapshot-1688975371-7541fd2"
// From the repo atala-prism-sdk https://github.com/input-output-hk/atala-prism-sdk/tree/node-1.4-extension-sdk
// deployed to github packages from sdk branch "node-1.4-extension-sdk"
// Also see https://github.com/input-output-hk/atala-prism-sdk/packages/920243
val vaultSdk = "0.1.0-build-2-96cc137d"
}

Expand Down Expand Up @@ -108,9 +111,9 @@ lazy val Dependencies = new {
// We have to exclude bouncycastle since for some reason bitcoinj depends on bouncycastle jdk15to18
// (i.e. JDK 1.5 to 1.8), but we are using JDK 11
val prismCredentials =
"io.iohk.atala" % "prism-credentials-jvm" % versions.prismSdk excludeAll ExclusionRule(
organization = "org.bouncycastle"
)
"io.iohk.atala" % "prism-credentials-jvm" % versions.prismSdk excludeAll ExclusionRule(organization =
"org.bouncycastle"
)
val prismProtos =
"io.iohk.atala" % "prism-protos-jvm" % versions.prismSdk % "protobuf-src" intransitive ()
val vaultProtos =
Expand Down Expand Up @@ -162,7 +165,7 @@ lazy val Dependencies = new {
val sttpDependencies = Seq(sttpCore, sttpCE2)
val tofuDependencies = Seq(tofu, tofuLogging, tofuDerevoTagless)
val prismDependencies =
Seq(prismCredentials, prismProtos, prismApi, vaultProtos)
Seq(prismCredentials, prismProtos, prismApi, vaultProtos, "io.iohk.atala.prism.apollo" % "apollo-jvm" % "1.2.13")
val scalapbDependencies = Seq(
"com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion % "protobuf",
"com.thesamet.scalapb" %% "scalapb-runtime-grpc" % scalapb.compiler.Version.scalapbVersion
Expand Down Expand Up @@ -190,10 +193,9 @@ lazy val commonSettings = Seq(
"-Ywarn-dead-code"
)
)
),
),
scalacOptions += "-Ymacro-annotations",
javacOptions ++= Seq("-source", "1.11", "-target", "1.11"),
githubTokenSource := TokenSource.Environment("GITHUB_TOKEN"),
resolvers += Resolver
.githubPackages("input-output-hk", "atala-prism-sdk"),
// Needed for Kotlin coroutines that support new memory management mode
Expand Down
215 changes: 215 additions & 0 deletions common/src/main/scala/identus/apollo/Apollo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package identus.apollo

// import io.iohk.atala.shared.models.HexString
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECNamedCurveSpec
// import zio._

import java.security.KeyFactory
import java.security.spec.{ECPrivateKeySpec, ECPublicKeySpec}
import scala.util.Try
import com.google.protobuf.ByteString

trait Apollo {
def secp256k1: Secp256k1KeyOps
def ed25519: Ed25519KeyOps
def x25519: X25519KeyOps
}

object Apollo {
// def layer: ULayer[Apollo] = ZLayer.succeed(default)

// TODO: migrate to KMP Apollo and support other key types
def default: Apollo = new Apollo {
override def secp256k1: Secp256k1KeyOps = Prism14Secp256k1Ops
override def ed25519: Ed25519KeyOps = ???
override def x25519: X25519KeyOps = ???
}
}

trait Encodable {
def getEncoded: Array[Byte]
}

trait Signable {
def sign(data: Array[Byte]): Array[Byte]
}

trait Verifiable {
def verify(data: Array[Byte], signature: Array[Byte]): Try[Unit]
}

/** Note this is a EC Public key */
trait PublicKey extends Encodable {
def curveName: String
def toCurvePoint: CurvePoint // FIXME
def getX: Array[Byte] = toCurvePoint.x
def getY: Array[Byte] = toCurvePoint.y

def getXAsByteString = ByteString.copyFrom(getX)
def getYAsByteString = ByteString.copyFrom(getY)

// TODO rename this
def getEncodedCompressed: Array[Byte] = ???
def getEncodedCompressedAsByteString = ByteString.copyFrom(getEncodedCompressed)

def toJavaPublicKey: java.security.interfaces.ECPublicKey = {
val point = toCurvePoint
val x = BigInt(point.x).bigInteger
val y = BigInt(point.y).bigInteger
val keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider())
val ecParameterSpec = ECNamedCurveTable.getParameterSpec(curveName)
val ecNamedCurveSpec = new ECNamedCurveSpec(
ecParameterSpec.getName(),
ecParameterSpec.getCurve(),
ecParameterSpec.getG(),
ecParameterSpec.getN()
)
val ecPublicKeySpec =
new ECPublicKeySpec(new java.security.spec.ECPoint(x, y), ecNamedCurveSpec)
keyFactory.generatePublic(ecPublicKeySpec).asInstanceOf[java.security.interfaces.ECPublicKey]
}
}

/** Note this is a EC Public key */
trait PrivateKey extends Encodable {
type Pub <: PublicKey
def toPublicKey: Pub
override final def toString(): String = "<REDACTED>"

def curveName: String
def getEncodedCompressed: Array[Byte] = ???

def toJavaPrivateKey: java.security.interfaces.ECPrivateKey = {
val bytes = getEncodedCompressed
val keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider())
val ecParameterSpec = ECNamedCurveTable.getParameterSpec(curveName)
val ecNamedCurveSpec = new ECNamedCurveSpec(
ecParameterSpec.getName(),
ecParameterSpec.getCurve(),
ecParameterSpec.getG(),
ecParameterSpec.getN()
)
val ecPrivateKeySpec = new ECPrivateKeySpec(new java.math.BigInteger(1, bytes), ecNamedCurveSpec)
keyFactory.generatePrivate(ecPrivateKeySpec).asInstanceOf[java.security.interfaces.ECPrivateKey]
}
}

object PublicKey {
def apply(curve: String, x: Array[Byte], y: Array[Byte]): PublicKey = ???
def apply(curve: String, data: Array[Byte]): PublicKey = {
curve match {
case "secp256k1" =>
val companion = new io.iohk.atala.prism.apollo.utils.KMMECSecp256k1PublicKey.Companion
val secp256k1PublicKey = companion.secp256k1FromBytes(data)
val point = secp256k1PublicKey.getCurvePoint()
PublicKey(curve, point.getX, point.getY)
case "ed25519" => ??? // FIXME TODO
case "x25519" => ??? // FIXME TODO
case _ => ???
}
}
}

object PrivateKey {}

trait MyKeyPair {
def publicKey: PublicKey
def privateKey: PrivateKey
}

object MyKeyPair {
def fromECKeyPair(pair: io.iohk.atala.prism.crypto.keys.ECKeyPair) = new MyKeyPair {
def publicKey: PublicKey = {
val p = pair.getPublicKey().getCurvePoint()
PublicKey(
io.iohk.atala.prism.crypto.ECConfig.INSTANCE.getCURVE_NAME(),
p.getX().bytes(),
p.getY().bytes()
)
}
def privateKey: PrivateKey = {
??? // FIXME
}
}

def generateKeyPair: MyKeyPair = ??? // FIXME EC.generateKeyPair()
// import io.iohk.atala.prism.crypto.EC.{INSTANCE => EC}
// import io.iohk.atala.prism.crypto.ECConfig.{INSTANCE => ECConfig}
// import io.iohk.atala.prism.crypto.keys.{ECKeyPair, ECPublicKey}
}

case class CurvePoint(x: Array[Byte], y: Array[Byte])

//FIXME
// enum DerivationPath {
// case Normal(i: Int) extends DerivationPath
// case Hardened(i: Int) extends DerivationPath
// }

final case class ECPoint(x: BigInt, y: BigInt)

// secp256k1
final case class Secp256k1KeyPair(publicKey: Secp256k1PublicKey, privateKey: Secp256k1PrivateKey) extends MyKeyPair

object Secp256k1KeyPair {
def generateKeyPair: Secp256k1KeyPair = ???
}
trait Secp256k1PublicKey extends PublicKey with Verifiable {
def getEncodedCompressed: Array[Byte]

def getEncodedUncompressed: Array[Byte]

def toCurvePoint: identus.apollo.CurvePoint
// def getECPoint: ECPoint

override final def hashCode(): Int = HexString.fromByteArray(getEncoded).hashCode()

override final def equals(x: Any): Boolean = x match {
case otherPK: Secp256k1PublicKey =>
HexString.fromByteArray(this.getEncoded) == HexString.fromByteArray(otherPK.getEncoded)
case _ => false
}

}
trait Secp256k1PrivateKey extends PrivateKey with Signable {
type Pub = Secp256k1PublicKey

override final def hashCode(): Int = HexString.fromByteArray(getEncoded).hashCode()

override final def equals(x: Any): Boolean = x match {
case otherPK: Secp256k1PrivateKey =>
HexString.fromByteArray(this.getEncoded) == HexString.fromByteArray(otherPK.getEncoded)
case _ => false
}
}
trait Secp256k1KeyOps {
def publicKeyFromEncoded(bytes: Array[Byte]): Try[Secp256k1PublicKey]
def privateKeyFromEncoded(bytes: Array[Byte]): Try[Secp256k1PrivateKey]
// def generateKeyPair: UIO[Secp256k1KeyPair]
// def randomBip32Seed: UIO[(Array[Byte], Seq[String])]
// def deriveKeyPair(seed: Array[Byte])(path: DerivationPath*): UIO[Secp256k1KeyPair]
}

// ed25519
final case class Ed25519KeyPair(publicKey: Ed25519PublicKey, privateKey: Ed25519PrivateKey)
trait Ed25519PublicKey extends PublicKey
trait Ed25519PrivateKey extends PrivateKey with Signable {
type Pub = Ed25519PublicKey
}
trait Ed25519KeyOps {
def publicKeyFromEncoded(bytes: Array[Byte]): Try[Ed25519PublicKey]
def privateKeyFromEncoded(bytes: Array[Byte]): Try[Ed25519PrivateKey]
}

// x25519
final case class X25519KeyPair(publicKey: X25519PublicKey, privateKey: X25519PrivateKey)
trait X25519PublicKey extends PublicKey
trait X25519PrivateKey extends PrivateKey {
type Pub = X25519PublicKey
}
trait X25519KeyOps {
def publicKeyFromEncoded(bytes: Array[Byte]): Try[X25519PublicKey]
def privateKeyFromEncoded(bytes: Array[Byte]): Try[X25519PrivateKey]
}
28 changes: 28 additions & 0 deletions common/src/main/scala/identus/apollo/BytesOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package identus.apollo

import java.nio.charset.StandardCharsets

object BytesOps {
private val HexArray = "0123456789abcdef".getBytes(StandardCharsets.US_ASCII);

// Converts hex string to bytes representation
def hexToBytes(hexEncoded: String): Array[Byte] = {
require(hexEncoded.length % 2 == 0, "Hex length needs to be even")
hexEncoded.grouped(2).toVector.map(hexToByte).toArray
}

// Converts bytes to hex string representation
def bytesToHex(bytes: Iterable[Byte]): String = {
val hexChars = new Array[Byte](bytes.size * 2)
for ((byte, i) <- bytes.zipWithIndex) {
val v = byte & 0xff
hexChars(i * 2) = HexArray(v >>> 4)
hexChars(i * 2 + 1) = HexArray(v & 0x0f)
}
new String(hexChars, StandardCharsets.UTF_8)
}

private def hexToByte(h: String): Byte = {
Integer.parseInt(h, 16).toByte
}
}
17 changes: 17 additions & 0 deletions common/src/main/scala/identus/apollo/HexString.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package identus.apollo

import scala.util.Try

// opaque type HexString = String

object HexString {
type HexString = String
def fromStringUnsafe(s: String): HexString = s
def fromString(s: String): Try[HexString] = Try(BytesOps.hexToBytes(s)).map(_ => s)
def fromByteArray(bytes: Array[Byte]): HexString = BytesOps.bytesToHex(bytes)

// extension(s: HexString) {
// def toByteArray: Array[Byte] = BytesOps.hexToBytes(s)
// def toString: String = s
// }
}
Loading
Loading