From 3e0d21d197dc822323276d3b448975e6d06ed84c Mon Sep 17 00:00:00 2001 From: Dima Date: Wed, 22 Aug 2018 11:26:10 +0300 Subject: [PATCH] Ecdsa recreate pair from secret (#2) * add possibility to re-create public key from private key for ECDSA alghorithm * change version * change version * add comments, change version of cats-effect * add comments, change method name * change comment * typo --- build.sbt | 12 ++++----- .../scala/fluence/crypto/ecdsa/Ecdsa.scala | 26 +++++++++++++++++++ .../scala/fluence/crypto/SignatureSpec.scala | 14 +++++++++- project/build.properties | 2 +- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 7d99411..ecd6a52 100644 --- a/build.sbt +++ b/build.sbt @@ -14,7 +14,7 @@ val scalaV = scalaVersion := "2.12.5" val commons = Seq( scalaV, - version := "0.0.1", + version := "0.0.2", fork in Test := true, parallelExecution in Test := false, organization := "one.fluence", @@ -33,13 +33,13 @@ commons val CodecV = "0.0.3" -val CatsEffectV = "1.0.0-RC" +val CatsEffectV = "1.0.0-RC3" val SloggingV = "0.6.1" val ScalatestV = "3.0.+" -val bouncyCastle = "org.bouncycastle" % "bcprov-jdk15on" % "1.59" +val bouncyCastle = "org.bouncycastle" % "bcprov-jdk15on" % "1.59" enablePlugins(AutomateHeaderPlugin) @@ -158,12 +158,12 @@ lazy val `crypto-jwt` = crossProject(JVMPlatform, JSPlatform) .settings( commons, libraryDependencies ++= Seq( - "one.fluence" %%% "codec-circe" % CodecV, - "org.scalatest" %%% "scalatest" % ScalatestV % Test + "one.fluence" %%% "codec-circe" % CodecV, + "org.scalatest" %%% "scalatest" % ScalatestV % Test ) ) .jsSettings( - fork in Test := false, + fork in Test := false, scalaJSModuleKind := ModuleKind.CommonJSModule ) .enablePlugins(AutomateHeaderPlugin) diff --git a/hashsign/jvm/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala b/hashsign/jvm/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala index 9f991b0..91a436f 100644 --- a/hashsign/jvm/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala +++ b/hashsign/jvm/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala @@ -23,6 +23,7 @@ import java.security.interfaces.ECPrivateKey import cats.Monad import cats.data.EitherT +import fluence.crypto.KeyPair.Secret import fluence.crypto.{KeyPair, _} import fluence.crypto.hash.JdkCryptoHasher import fluence.crypto.signature.{SignAlgo, SignatureChecker, Signer} @@ -72,6 +73,31 @@ class Ecdsa(curveType: String, scheme: String, hasher: Option[Crypto.Hasher[Arra } yield keyPair } + /** + * Restores pair of keys from the known secret key. + * The public key will be the same each method call with the same secret key. + * @param sk secret key + * @return key pair + */ + def restorePairFromSecret[F[_]: Monad](sk: Secret): EitherT[F, CryptoError, KeyPair] = + for { + ecSpec ← EitherT.fromOption( + Option(ECNamedCurveTable.getParameterSpec(curveType)), + CryptoError("Parameter spec for the curve is not available.") + ) + keyPair ← nonFatalHandling { + val hex = sk.value.toHex + val d = new BigInteger(hex, HEXradix) + // to re-create public key from private we need to multiply known from curve point G with D (private key) + // result will be point Q (public key) + // https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm + val g = ecSpec.getG + val q = g.multiply(d) + val pk = ByteVector(q.getEncoded(true)) + KeyPair.fromByteVectors(pk, sk.value) + }("Could not generate KeyPair from private key. Unexpected.") + } yield keyPair + def sign[F[_]: Monad]( keyPair: KeyPair, message: ByteVector diff --git a/hashsign/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala b/hashsign/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala index 52e5b0a..f4bcbfd 100644 --- a/hashsign/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala +++ b/hashsign/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala @@ -18,6 +18,7 @@ package fluence.crypto import java.io.File +import java.math.BigInteger import cats.data.EitherT import cats.instances.try_._ @@ -31,7 +32,7 @@ import scala.util.{Random, Try} class SignatureSpec extends WordSpec with Matchers { - def rndBytes(size: Int) = Random.nextString(10).getBytes + def rndBytes(size: Int): Array[Byte] = Random.nextString(10).getBytes def rndByteVector(size: Int) = ByteVector(rndBytes(size)) @@ -125,5 +126,16 @@ class SignatureSpec extends WordSpec with Matchers { //try to store key into previously created file storage.storeKeyPair(keys).attempt.unsafeRunSync().isLeft shouldBe true } + + "restore key pair from secret key" in { + val algo = Ecdsa.signAlgo + val testKeys = algo.generateKeyPair.unsafe(None) + + val ecdsa = Ecdsa.ecdsa_secp256k1_sha256 + + val newKeys = ecdsa.restorePairFromSecret(testKeys.secretKey).extract + + testKeys shouldBe newKeys + } } } diff --git a/project/build.properties b/project/build.properties index 90e7013..2daea37 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 1.1.4 +sbt.version = 1.2.1