From 16f5a9356209a9b520617c3660fb64a8cc921006 Mon Sep 17 00:00:00 2001 From: Dima Date: Tue, 2 Jul 2019 12:49:00 +0300 Subject: [PATCH] ed25519 for js (#8) --- build.sbt | 8 +- .../scala/fluence/crypto/aes/AesCrypt.scala | 8 +- .../scala/fluence/crypto/aes/AesCrypt.scala | 8 +- .../scala/fluence/crypto/CryptoError.scala | 1 + .../fluence/crypto/CryptoJsHelpers.scala | 29 +++++ .../scala/fluence/crypto/ecdsa/Ecdsa.scala | 38 +++--- .../fluence/crypto/ed25519/Ed25519.scala | 110 ++++++++++++++++++ .../crypto/facade/ed25519/Supercop.scala | 38 ++++++ .../fluence/crypto/hash/JsCryptoHasher.scala | 27 +++++ .../scala/fluence/crypto/Ed25519Spec.scala | 97 +++++++++++++++ .../{EcdsaSpec.scala => EllipticSpec.scala} | 4 +- .../scala/fluence/crypto/ecdsa/Ecdsa.scala | 12 +- .../scala/fluence/crypto/eddsa/Ed25519.scala | 18 ++- .../scala/fluence/crypto/Ed25519Spec.scala | 14 +-- project/FluenceCrossType.scala | 1 - project/plugins.sbt | 2 +- 16 files changed, 367 insertions(+), 48 deletions(-) create mode 100644 hashsign/js/src/main/scala/fluence/crypto/CryptoJsHelpers.scala create mode 100644 hashsign/js/src/main/scala/fluence/crypto/ed25519/Ed25519.scala create mode 100644 hashsign/js/src/main/scala/fluence/crypto/facade/ed25519/Supercop.scala create mode 100644 hashsign/js/src/test/scala/fluence/crypto/Ed25519Spec.scala rename hashsign/js/src/test/scala/fluence/crypto/{EcdsaSpec.scala => EllipticSpec.scala} (96%) diff --git a/build.sbt b/build.sbt index 3e4b2af..1f06352 100644 --- a/build.sbt +++ b/build.sbt @@ -14,7 +14,7 @@ val scalaV = scalaVersion := "2.12.8" val commons = Seq( scalaV, - version := "0.0.6", + version := "0.0.7", fork in Test := true, parallelExecution in Test := false, organization := "one.fluence", @@ -26,7 +26,7 @@ val commons = Seq( bintrayOrganization := Some("fluencelabs"), publishMavenStyle := true, bintrayRepository := "releases", - resolvers += Resolver.bintrayRepo("fluencelabs", "releases") + resolvers ++= Seq(Resolver.bintrayRepo("fluencelabs", "releases"), Resolver.sonatypeRepo("releases")) ) commons @@ -103,8 +103,10 @@ lazy val `crypto-hashsign` = crossProject(JVMPlatform, JSPlatform) ) ) .jsSettings( + libraryDependencies += "io.scalajs" %%% "nodejs" % "0.4.2", npmDependencies in Compile ++= Seq( - "elliptic" -> "6.4.1" + "elliptic" -> "6.4.1", + "supercop.js" -> "2.0.1" ), scalaJSModuleKind in Test := ModuleKind.CommonJSModule, //all JavaScript dependencies will be concatenated to a single file *-jsdeps.js diff --git a/cipher/js/src/main/scala/fluence/crypto/aes/AesCrypt.scala b/cipher/js/src/main/scala/fluence/crypto/aes/AesCrypt.scala index e7f4ee2..0462e69 100644 --- a/cipher/js/src/main/scala/fluence/crypto/aes/AesCrypt.scala +++ b/cipher/js/src/main/scala/fluence/crypto/aes/AesCrypt.scala @@ -86,7 +86,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) { //IV also needs to be transformed in byte array val byteIv = iv.map(i ⇒ ByteVector.fromValidHex(i.toString)) byteIv.map(_.toArray ++ crypted.toArray).getOrElse(crypted.toArray) - }("Cannot encrypt data.") + }("Cannot encrypt data") } private def decryptData[F[_]: Monad](key: Key, base64Data: String, iv: Option[String]) = { @@ -95,7 +95,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) { val cryptOptions = CryptOptions(iv = iv.map(i ⇒ CryptoJS.enc.Hex.parse(i)), padding = pad, mode = mode) val dec = aes.decrypt(base64Data, key, cryptOptions) ByteVector.fromValidHex(dec.toString) - }("Cannot decrypt data.") + }("Cannot decrypt data") } /** @@ -112,7 +112,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) { val (ivOp, data) = dataWithParams val base64 = ByteVector(data).toBase64 (ivOp, base64) - }("Cannot detach data and IV.") + }("Cannot detach data and IV") } /** @@ -123,7 +123,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) { // get raw key from password and salt val keyOption = KeyOptions(BITS, iterations = iterationCount, hasher = CryptoJS.algo.SHA256) CryptoJS.PBKDF2(new String(password), salt, keyOption) - }("Cannot init secret key.") + }("Cannot init secret key") } } diff --git a/cipher/jvm/src/main/scala/fluence/crypto/aes/AesCrypt.scala b/cipher/jvm/src/main/scala/fluence/crypto/aes/AesCrypt.scala index 4471f1b..643166e 100644 --- a/cipher/jvm/src/main/scala/fluence/crypto/aes/AesCrypt.scala +++ b/cipher/jvm/src/main/scala/fluence/crypto/aes/AesCrypt.scala @@ -113,7 +113,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) extend ): EitherT[F, CryptoError, Array[Byte]] = nonFatalHandling { PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(password) - }("Cannot init secret key.") + }("Cannot init secret key") /** * Setup AES CBC cipher @@ -132,7 +132,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) extend cipher.init(encrypt, params) cipher - }("Cannot setup aes cipher.") + }("Cannot setup aes cipher") } private def cipherBytes[F[_]: Monad]( @@ -146,7 +146,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) extend val lastBlockLength = cipher.doFinal(buf, outputLength) //remove padding buf.slice(0, outputLength + lastBlockLength) - }("Error in cipher processing.") + }("Error in cipher processing") } /** @@ -176,7 +176,7 @@ class AesCrypt(password: Array[Char], withIV: Boolean, config: AesConfig) extend val ivData = data.slice(0, ivSize) val encData = data.slice(ivSize, data.length) DetachedData(ivData, encData) - }("Cannot detach data and IV.") + }("Cannot detach data and IV") } private def paramsWithIV[F[_]: Monad]( diff --git a/core/src/main/scala/fluence/crypto/CryptoError.scala b/core/src/main/scala/fluence/crypto/CryptoError.scala index c5871ec..2021afe 100644 --- a/core/src/main/scala/fluence/crypto/CryptoError.scala +++ b/core/src/main/scala/fluence/crypto/CryptoError.scala @@ -20,6 +20,7 @@ package fluence.crypto import cats.Applicative import cats.data.EitherT +import scala.language.higherKinds import scala.util.control.{NoStackTrace, NonFatal} case class CryptoError(message: String, causedBy: Option[Throwable] = None) extends NoStackTrace { diff --git a/hashsign/js/src/main/scala/fluence/crypto/CryptoJsHelpers.scala b/hashsign/js/src/main/scala/fluence/crypto/CryptoJsHelpers.scala new file mode 100644 index 0000000..3341580 --- /dev/null +++ b/hashsign/js/src/main/scala/fluence/crypto/CryptoJsHelpers.scala @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 Fluence Labs Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package fluence.crypto + +import io.scalajs.nodejs.buffer.Buffer +import scodec.bits.ByteVector + +import scala.language.higherKinds + +object CryptoJsHelpers { + implicit class ByteVectorOp(bv: ByteVector) { + def toJsBuffer: Buffer = Buffer.from(bv.toHex, "hex") + } +} diff --git a/hashsign/js/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala b/hashsign/js/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala index b089ddb..bcf789b 100644 --- a/hashsign/js/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala +++ b/hashsign/js/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala @@ -37,38 +37,46 @@ import scala.scalajs.js.typedarray.Uint8Array class Ecdsa(ec: EC, hasher: Option[Crypto.Hasher[Array[Byte], Array[Byte]]]) { import CryptoError.nonFatalHandling + /** + * Restores key pair by secret key. + * + */ + def restoreKeyPair[F[_]](secretKey: KeyPair.Secret)(implicit F: Monad[F]): EitherT[F, CryptoError, KeyPair] = { + for { + secret ← nonFatalHandling { + val key = ec.keyFromPrivate(secretKey.value.toHex, "hex") + val publicHex = key.getPublic(compact = true, "hex") + val secretHex = key.getPrivate("hex") + val public = ByteVector.fromValidHex(publicHex) + val secret = ByteVector.fromValidHex(secretHex) + KeyPair.fromByteVectors(public, secret) + }("Incorrect secret key format") + } yield secret + } + val generateKeyPair: Crypto.KeyPairGenerator = new Crypto.Func[Option[Array[Byte]], KeyPair] { override def apply[F[_]](input: Option[Array[Byte]])(implicit F: Monad[F]): EitherT[F, CryptoError, KeyPair] = nonFatalHandling { val seedJs = input.map(bs ⇒ js.Dynamic.literal(entropy = bs.toJSArray)) val key = ec.genKeyPair(seedJs) - val publicHex = key.getPublic(true, "hex") + val publicHex = key.getPublic(compact = true, "hex") val secretHex = key.getPrivate("hex") val public = ByteVector.fromValidHex(publicHex) val secret = ByteVector.fromValidHex(secretHex) KeyPair.fromByteVectors(public, secret) - }("Failed to generate key pair.") + }("Failed to generate key pair") } def sign[F[_]: Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoError, Signature] = for { secret ← nonFatalHandling { ec.keyFromPrivate(keyPair.secretKey.value.toHex, "hex") - }("Cannot get private key from key pair.") - hash ← hash(message) + }("Cannot get private key from key pair") + hash ← JsCryptoHasher.hashJs(message, hasher) signHex ← nonFatalHandling(secret.sign(new Uint8Array(hash)).toDER("hex"))("Cannot sign message") } yield Signature(ByteVector.fromValidHex(signHex)) - def hash[F[_]: Monad](message: ByteVector): EitherT[F, CryptoError, js.Array[Byte]] = { - val arr = message.toArray - hasher - .fold(EitherT.pure[F, CryptoError](arr)) { h ⇒ - h[F](arr) - } - .map(_.toJSArray) - } - def verify[F[_]: Monad]( pubKey: KeyPair.Public, signature: Signature, @@ -79,8 +87,8 @@ class Ecdsa(ec: EC, hasher: Option[Crypto.Hasher[Array[Byte], Array[Byte]]]) { val hex = pubKey.value.toHex ec.keyFromPublic(hex, "hex") }("Incorrect public key format.") - hash ← hash(message) - verify ← nonFatalHandling(public.verify(new Uint8Array(hash), signature.sign.toHex))("Cannot verify message.") + hash ← JsCryptoHasher.hashJs(message, hasher) + verify ← nonFatalHandling(public.verify(new Uint8Array(hash), signature.sign.toHex))("Cannot verify message") _ ← EitherT.cond[F](verify, (), CryptoError("Signature is not verified")) } yield () } diff --git a/hashsign/js/src/main/scala/fluence/crypto/ed25519/Ed25519.scala b/hashsign/js/src/main/scala/fluence/crypto/ed25519/Ed25519.scala new file mode 100644 index 0000000..e1ca955 --- /dev/null +++ b/hashsign/js/src/main/scala/fluence/crypto/ed25519/Ed25519.scala @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 Fluence Labs Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package fluence.crypto.ed25519 + +import cats.Monad +import cats.data.EitherT +import fluence.crypto.CryptoError.nonFatalHandling +import fluence.crypto.facade.ed25519.Supercop +import fluence.crypto.hash.JsCryptoHasher +import fluence.crypto.{Crypto, CryptoError, KeyPair, CryptoJsHelpers} +import fluence.crypto.signature.{SignAlgo, Signature, SignatureChecker, Signer} +import io.scalajs.nodejs.buffer.Buffer +import scodec.bits.ByteVector + +import scala.language.higherKinds + +class Ed25519(hasher: Option[Crypto.Hasher[Array[Byte], Array[Byte]]]) { + + import CryptoJsHelpers._ + + def sign[F[_]: Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoError, Signature] = + for { + hash ← JsCryptoHasher.hash(message, hasher) + sign ← nonFatalHandling { + Supercop.sign( + Buffer.from(ByteVector(hash).toHex, "hex"), + keyPair.publicKey.value.toJsBuffer, + keyPair.secretKey.value.toJsBuffer + ) + }("Error on signing message by js/ed25519 signature") + } yield Signature(ByteVector.fromValidHex(sign.toString("hex"))) + + def verify[F[_]: Monad]( + pubKey: KeyPair.Public, + signature: Signature, + message: ByteVector + ): EitherT[F, CryptoError, Unit] = + for { + hash ← JsCryptoHasher.hash(message, hasher) + verify ← nonFatalHandling( + Supercop.verify(signature.sign.toJsBuffer, ByteVector(hash).toJsBuffer, pubKey.value.toJsBuffer) + )("Cannot verify message") + _ ← EitherT.cond[F](verify, (), CryptoError("Signature is not verified")) + } yield () + + val generateKeyPair: Crypto.KeyPairGenerator = + new Crypto.Func[Option[Array[Byte]], KeyPair] { + override def apply[F[_]]( + input: Option[Array[Byte]] + )(implicit F: Monad[F]): EitherT[F, CryptoError, KeyPair] = { + for { + seed ← nonFatalHandling(input.map(ByteVector(_).toJsBuffer).getOrElse(Supercop.createSeed()))( + "Error on seed creation" + ) + jsKeyPair ← nonFatalHandling(Supercop.createKeyPair(seed))("Error on key pair generation.") + keyPair ← nonFatalHandling( + KeyPair.fromByteVectors( + ByteVector.fromValidHex(jsKeyPair.publicKey.toString("hex")), + ByteVector.fromValidHex(jsKeyPair.secretKey.toString("hex")) + ) + )("Error on decoding public and secret keys") + } yield keyPair + } + } +} + +object Ed25519 { + + val ed25519: Ed25519 = new Ed25519(Some(JsCryptoHasher.Sha256)) + + val signAlgo: SignAlgo = { + SignAlgo( + name = "ed25519", + generateKeyPair = ed25519.generateKeyPair, + signer = kp ⇒ + Signer( + kp.publicKey, + new Crypto.Func[ByteVector, Signature] { + override def apply[F[_]]( + input: ByteVector + )(implicit F: Monad[F]): EitherT[F, CryptoError, Signature] = + ed25519.sign(kp, input) + } + ), + checker = pk ⇒ + new SignatureChecker { + override def check[F[_]: Monad]( + signature: fluence.crypto.signature.Signature, + plain: ByteVector + ): EitherT[F, CryptoError, Unit] = + ed25519.verify(pk, signature, plain) + } + ) + } +} diff --git a/hashsign/js/src/main/scala/fluence/crypto/facade/ed25519/Supercop.scala b/hashsign/js/src/main/scala/fluence/crypto/facade/ed25519/Supercop.scala new file mode 100644 index 0000000..1e7699e --- /dev/null +++ b/hashsign/js/src/main/scala/fluence/crypto/facade/ed25519/Supercop.scala @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Fluence Labs Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package fluence.crypto.facade.ed25519 + +import io.scalajs.nodejs.buffer.Buffer + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSImport + +@js.native +trait KeyPair extends js.Object { + val publicKey: Buffer + val secretKey: Buffer +} + +@js.native +@JSImport("supercop.js", JSImport.Namespace) +object Supercop extends js.Object { + def verify(sig: Buffer, msg: Buffer, pubKey: Buffer): Boolean = js.native + def sign(msg: Buffer, pubKey: Buffer, privKey: Buffer): Buffer = js.native + def createKeyPair(seed: Buffer): KeyPair = js.native + def createSeed(): Buffer = js.native +} diff --git a/hashsign/js/src/main/scala/fluence/crypto/hash/JsCryptoHasher.scala b/hashsign/js/src/main/scala/fluence/crypto/hash/JsCryptoHasher.scala index 314cda9..ca5f9da 100644 --- a/hashsign/js/src/main/scala/fluence/crypto/hash/JsCryptoHasher.scala +++ b/hashsign/js/src/main/scala/fluence/crypto/hash/JsCryptoHasher.scala @@ -17,10 +17,14 @@ package fluence.crypto.hash +import cats.Monad +import cats.data.EitherT import fluence.crypto.{Crypto, CryptoError} import fluence.crypto.facade.ecdsa.{SHA1, SHA256} import scodec.bits.ByteVector +import scala.language.higherKinds +import scala.scalajs.js import scala.scalajs.js.JSConverters._ import scala.scalajs.js.typedarray.Uint8Array import scala.util.Try @@ -44,4 +48,27 @@ object JsCryptoHasher { ByteVector.fromValidHex(sha1.digest("hex")).toArray }.toEither.left.map(err ⇒ CryptoError("Cannot calculate Sha256 hash", Some(err))) } + + /** + * Calculates hash of message. + * + * @return hash in JS array + */ + def hashJs[F[_]: Monad](message: ByteVector, hasher: Option[Crypto.Hasher[Array[Byte], Array[Byte]]]): EitherT[F, CryptoError, js.Array[Byte]] = { + hash(message, hasher) + .map(_.toJSArray) + } + + /** + * Calculates hash of message. + * + * @return hash in Scala array + */ + def hash[F[_]: Monad](message: ByteVector, hasher: Option[Crypto.Hasher[Array[Byte], Array[Byte]]]): EitherT[F, CryptoError, Array[Byte]] = { + val arr = message.toArray + hasher + .fold(EitherT.pure[F, CryptoError](arr)) { h ⇒ + h[F](arr) + } + } } diff --git a/hashsign/js/src/test/scala/fluence/crypto/Ed25519Spec.scala b/hashsign/js/src/test/scala/fluence/crypto/Ed25519Spec.scala new file mode 100644 index 0000000..3a42374 --- /dev/null +++ b/hashsign/js/src/test/scala/fluence/crypto/Ed25519Spec.scala @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 Fluence Labs Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package fluence.crypto + +import cats.data.EitherT +import fluence.crypto.signature.Signature +import org.scalatest.{Matchers, WordSpec} +import scodec.bits.ByteVector +import cats.instances.try_._ +import fluence.crypto.ed25519.Ed25519 + +import scala.util.{Random, Try} + +class Ed25519Spec extends WordSpec with Matchers { + def rndBytes(size: Int): Array[Byte] = Random.nextString(10).getBytes + + def rndByteVector(size: Int) = ByteVector(rndBytes(size)) + + private implicit class TryEitherTExtractor[A <: Throwable, B](et: EitherT[Try, A, B]) { + + def extract: B = + et.value.map { + case Left(e) ⇒ fail(e) // for making test fail message more describable + case Right(v) ⇒ v + }.get + + def isOk: Boolean = et.value.fold(_ ⇒ false, _.isRight) + } + + "ed25519 algorithm" should { + "correct sign and verify data" in { + val algorithm = Ed25519.ed25519 + + val keys = algorithm.generateKeyPair.unsafe(None) + val pubKey = keys.publicKey + val data = rndByteVector(10) + val sign = algorithm.sign[Try](keys, data).extract + + algorithm.verify[Try](pubKey, sign, data).isOk shouldBe true + + val randomData = rndByteVector(10) + val randomSign = algorithm.sign(keys, randomData).extract + + algorithm.verify(pubKey, randomSign, data).isOk shouldBe false + + algorithm.verify(pubKey, sign, randomData).isOk shouldBe false + } + + "correctly work with signer and checker" in { + val algo = Ed25519.signAlgo + val keys = algo.generateKeyPair.unsafe(None) + val signer = algo.signer(keys) + val checker = algo.checker(keys.publicKey) + + val data = rndByteVector(10) + val sign = signer.sign(data).extract + + checker.check(sign, data).isOk shouldBe true + + val randomSign = signer.sign(rndByteVector(10)).extract + checker.check(randomSign, data).isOk shouldBe false + } + + "throw an errors on invalid data" in { + val algo = Ed25519.signAlgo + val keys = algo.generateKeyPair.unsafe(None) + val signer = algo.signer(keys) + val checker = algo.checker(keys.publicKey) + val data = rndByteVector(10) + + val sign = signer.sign(data).extract + + the[CryptoError] thrownBy checker.check(Signature(rndByteVector(10)), data).value.flatMap(_.toTry).get + val invalidChecker = algo.checker(KeyPair.fromByteVectors(rndByteVector(10), rndByteVector(10)).publicKey) + the[CryptoError] thrownBy invalidChecker + .check(sign, data) + .value + .flatMap(_.toTry) + .get + } + } +} diff --git a/hashsign/js/src/test/scala/fluence/crypto/EcdsaSpec.scala b/hashsign/js/src/test/scala/fluence/crypto/EllipticSpec.scala similarity index 96% rename from hashsign/js/src/test/scala/fluence/crypto/EcdsaSpec.scala rename to hashsign/js/src/test/scala/fluence/crypto/EllipticSpec.scala index 8ddffae..e38c9e7 100644 --- a/hashsign/js/src/test/scala/fluence/crypto/EcdsaSpec.scala +++ b/hashsign/js/src/test/scala/fluence/crypto/EllipticSpec.scala @@ -26,9 +26,9 @@ import scodec.bits.ByteVector import scala.util.{Random, Try} -class EcdsaSpec extends WordSpec with Matchers { +class EllipticSpec 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)) 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 96b7e61..24d1de0 100644 --- a/hashsign/jvm/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala +++ b/hashsign/jvm/src/main/scala/fluence/crypto/ecdsa/Ecdsa.scala @@ -142,36 +142,36 @@ class Ecdsa(curveType: String, scheme: String, hasher: Option[Crypto.Hasher[Arra ): EitherT[F, CryptoError, Unit] = for { ec ← curveSpec - keySpec ← nonFatalHandling(new ECPublicKeySpec(ec.getCurve.decodePoint(publicKey), ec))("Cannot read public key.") + keySpec ← nonFatalHandling(new ECPublicKeySpec(ec.getCurve.decodePoint(publicKey), ec))("Cannot read public key") keyFactory ← getKeyFactory signProvider ← getSignatureProvider verify ← nonFatalHandling { signProvider.initVerify(keyFactory.generatePublic(keySpec)) signProvider.update(hasher.map(_.unsafe(message)).getOrElse(message)) signProvider.verify(signature) - }("Cannot verify message.") + }("Cannot verify message") _ ← EitherT.cond[F](verify, (), CryptoError("Signature is not verified")) } yield () private def curveSpec[F[_]: Monad] = nonFatalHandling(ECNamedCurveTable.getParameterSpec(curveType).asInstanceOf[ECParameterSpec])( - "Cannot get curve parameters." + "Cannot get curve parameters" ) private def getKeyPairGenerator[F[_]: Monad] = nonFatalHandling(KeyPairGenerator.getInstance(ECDSA, BouncyCastleProvider.PROVIDER_NAME))( - "Cannot get key pair generator." + "Cannot get key pair generator" ) private def getKeyFactory[F[_]: Monad] = nonFatalHandling(KeyFactory.getInstance(ECDSA, BouncyCastleProvider.PROVIDER_NAME))( - "Cannot get key factory instance." + "Cannot get key factory instance" ) private def getSignatureProvider[F[_]: Monad] = nonFatalHandling(Signature.getInstance(scheme, BouncyCastleProvider.PROVIDER_NAME))( - "Cannot get signature instance." + "Cannot get signature instance" ) } diff --git a/hashsign/jvm/src/main/scala/fluence/crypto/eddsa/Ed25519.scala b/hashsign/jvm/src/main/scala/fluence/crypto/eddsa/Ed25519.scala index 55cda42..02d07fd 100644 --- a/hashsign/jvm/src/main/scala/fluence/crypto/eddsa/Ed25519.scala +++ b/hashsign/jvm/src/main/scala/fluence/crypto/eddsa/Ed25519.scala @@ -137,13 +137,21 @@ object Ed25519 { /** * Keys in tendermint are generating with a random seed of 32 bytes */ - val tendermintEd25519 = new Ed25519(256) - val tendermintAlgo: SignAlgo = signAlgo(256) + val ed25519 = new Ed25519(256) + val signAlgo: SignAlgo = signAlgoInit(256) - def ed25519(strength: Int) = new Ed25519(strength) + /** + * + * @param strength the size, in bits, of the keys we want to produce + */ + def ed25519Init(strength: Int) = new Ed25519(strength) - def signAlgo(strength: Int): SignAlgo = { - val algo = ed25519(strength) + /** + * + * @param strength the size, in bits, of the keys we want to produce + */ + def signAlgoInit(strength: Int): SignAlgo = { + val algo = ed25519Init(strength) SignAlgo( name = "ed25519", generateKeyPair = algo.generateKeyPair, diff --git a/hashsign/jvm/src/test/scala/fluence/crypto/Ed25519Spec.scala b/hashsign/jvm/src/test/scala/fluence/crypto/Ed25519Spec.scala index e862302..9e0a422 100644 --- a/hashsign/jvm/src/test/scala/fluence/crypto/Ed25519Spec.scala +++ b/hashsign/jvm/src/test/scala/fluence/crypto/Ed25519Spec.scala @@ -48,7 +48,7 @@ class Ed25519Spec extends WordSpec with Matchers { "ed25519 algorithm" should { "correct sign and verify data" in { - val algorithm = Ed25519.ed25519(32) + val algorithm = Ed25519.ed25519Init(32) val keys = algorithm.generateKeyPair.unsafe(None) val pubKey = keys.publicKey @@ -66,7 +66,7 @@ class Ed25519Spec extends WordSpec with Matchers { } "correctly work with signer and checker" in { - val algo = Ed25519.signAlgo(32) + val algo = Ed25519.signAlgo val keys = algo.generateKeyPair.unsafe(None) val signer = algo.signer(keys) val checker = algo.checker(keys.publicKey) @@ -81,7 +81,7 @@ class Ed25519Spec extends WordSpec with Matchers { } "throw an errors on invalid data" in { - val algo = Ed25519.signAlgo(32) + val algo = Ed25519.signAlgo val keys = algo.generateKeyPair.unsafe(None) val signer = algo.signer(keys) val checker = algo.checker(keys.publicKey) @@ -103,7 +103,7 @@ class Ed25519Spec extends WordSpec with Matchers { } "store and read key from file" in { - val algo = Ed25519.signAlgo(32) + val algo = Ed25519.signAlgo val keys = algo.generateKeyPair.unsafe(None) val keyFile = File.createTempFile("test", "") @@ -127,10 +127,10 @@ class Ed25519Spec extends WordSpec with Matchers { } "restore key pair from secret key" in { - val algo = Ed25519.signAlgo(32) + val algo = Ed25519.signAlgo val testKeys = algo.generateKeyPair.unsafe(None) - val ed25519 = Ed25519.ed25519(32) + val ed25519 = Ed25519.ed25519 val newKeys = ed25519.restorePairFromSecret(testKeys.secretKey).extract @@ -158,7 +158,7 @@ class Ed25519Spec extends WordSpec with Matchers { val privKey = ByteVector.fromBase64Descriptive(privKeyBase64).right.get val pubKey = ByteVector.fromBase64Descriptive(pubKeyBase64).right.get - val restored = Ed25519.tendermintEd25519 + val restored = Ed25519.ed25519 .restorePairFromSecret[Try](KeyPair.Secret(privKey.dropRight(32))) .value .get diff --git a/project/FluenceCrossType.scala b/project/FluenceCrossType.scala index 74e64e6..acb39c5 100644 --- a/project/FluenceCrossType.scala +++ b/project/FluenceCrossType.scala @@ -26,4 +26,3 @@ object FluenceCrossType extends sbtcrossproject.CrossType { override def sharedSrcDir(projectBase: File, conf: String) = Some(shared(projectBase, conf)) } - diff --git a/project/plugins.sbt b/project/plugins.sbt index 54f9b7b..e5413c4 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -4,7 +4,7 @@ addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.20") addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.2.0") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.27") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.28") addSbtPlugin("org.portable-scala" % "sbt-crossproject" % "0.6.0") addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.6.0")