Ecdsa for scalajs (#37)

* refactor sign classes
store in private key only S, in public key only Q point without wrappers

* refactor, class ...[F[_]] to method ...[F[_]]

* remove redundant

* finally js crypto === java crypto

* facade for hash, EcdsaJS implementation, rewrite crypto logic to hex for cross-platform compatibility

* clean branch

* delete Main, add headers

* merge master

* small fixes

* todo

* refactoring, comments

* alphabet

* merge master

* fix tests

* fix PR comments

* fix PR comments
This commit is contained in:
Dima 2018-02-13 17:59:17 +03:00 committed by GitHub
parent 17a5930399
commit d182a92e98
12 changed files with 397 additions and 96 deletions

View File

@ -0,0 +1,86 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package fluence.crypto.algorithm
import cats.data.EitherT
import cats.{ Monad, MonadError }
import fluence.crypto.facade.EC
import fluence.crypto.hash.{ CryptoHasher, JsCryptoHasher }
import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.Signature
import scodec.bits.ByteVector
import scala.language.higherKinds
import scala.scalajs.js
import scala.scalajs.js.JSConverters._
/**
* Return in all js methods hex, because in the other case we will receive javascript objects
* @param ec implementation of ecdsa logic for different curves
*/
class EcdsaJS(ec: EC, hasher: Option[CryptoHasher[Array[Byte], Array[Byte]]]) extends Algorithm with SignatureFunctions with KeyGenerator {
import CryptoErr._
override def generateKeyPair[F[_] : Monad](seed: Option[Array[Byte]] = None): EitherT[F, CryptoErr, KeyPair] = {
nonFatalHandling {
val seedJs = seed.map(bs js.Dynamic.literal(entropy = bs.toJSArray))
val key = ec.genKeyPair(seedJs)
val publicHex = key.getPublic(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.")
}
override def sign[F[_] : Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, Signature] = {
for {
secret nonFatalHandling{
ec.keyFromPrivate(keyPair.secretKey.value.toHex, "hex")
}("Cannot get private key from key pair.")
hash hash(message)
signHex nonFatalHandling(secret.sign(hash).toDER("hex"))("Cannot sign message")
} yield Signature(keyPair.publicKey, ByteVector.fromValidHex(signHex))
}
def hash[F[_] : Monad](message: ByteVector): EitherT[F, CryptoErr, js.Array[Byte]] = {
val arr = message.toArray
hasher.fold(EitherT.pure[F, CryptoErr](arr)) { h
nonFatalHandling {
h.hash(message.toArray)
}("Cannot hash message.")
}.map(_.toJSArray)
}
override def verify[F[_] : Monad](signature: Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] = {
for {
public nonFatalHandling{
val hex = signature.publicKey.value.toHex
ec.keyFromPublic(hex, "hex")
}("Incorrect public key format.")
hash hash(message)
verify nonFatalHandling(public.verify(hash, signature.sign.toHex))("Cannot verify message.")
_ EitherT.cond[F](verify, (), CryptoErr("Signature is not verified"))
} yield ()
}
}
object EcdsaJS {
def ecdsa_secp256k1_sha256 = new EcdsaJS(new EC("secp256k1"), Some(JsCryptoHasher.Sha256))
}

View File

@ -0,0 +1,52 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package fluence.crypto.facade
import scala.scalajs.js
import scala.scalajs.js.annotation._
//TODO hide enc argument in methods, make it `hex` by default
/**
* https://github.com/indutny/elliptic - fast elliptic-curve cryptography in a plain javascript implementation
*/
@js.native
@JSImport("elliptic", "ec")
class EC(curve: String) extends js.Object {
def genKeyPair(options: Option[js.Dynamic] = None): KeyPair = js.native
def keyPair(options: js.Dynamic): KeyPair = js.native
def keyFromPublic(pub: String, enc: String): KeyPair = js.native
def keyFromPrivate(priv: String, enc: String): KeyPair = js.native
}
@js.native
@JSImport("elliptic", "ec")
class KeyPair(ec: EC, options: js.Dynamic) extends js.Object {
def verify(msg: js.Array[Byte], signature: String): Boolean = js.native
def sign(msg: js.Array[Byte]): Signature = js.native
def getPublic(compact: Boolean, enc: String): String = js.native
def getPrivate(enc: String): String = js.native
val priv: js.Any = js.native
val pub: js.Any = js.native
}
@js.native
@JSImport("elliptic", "ec")
class Signature(der: String, enc: String = "hex") extends js.Object {
def toDER(enc: String): String = js.native
}

View File

@ -15,8 +15,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
object Main { package fluence.crypto.facade
def main(args: Array[String]): Unit = {
println("Hello world!") import scala.scalajs.js
} import scala.scalajs.js.annotation.JSImport
//TODO hide enc argument in methods, make it `hex` by default
/**
* https://github.com/indutny/hash.js - part of elliptic library
*/
@js.native
@JSImport("hash.js", "sha256")
class SHA256() extends js.Object {
def update(msg: js.Array[Byte]): Unit = js.native
def digest(enc: String): String = js.native
} }

View File

@ -0,0 +1,40 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package fluence.crypto.hash
import fluence.crypto.facade.SHA256
import scodec.bits.ByteVector
import scala.scalajs.js.JSConverters._
object JsCryptoHasher {
lazy val Sha256: CryptoHasher[Array[Byte], Array[Byte]] = new CryptoHasher[Array[Byte], Array[Byte]] {
override def hash(msg1: Array[Byte]): Array[Byte] = {
val sha256 = new SHA256()
sha256.update(msg1.toJSArray)
ByteVector.fromValidHex(sha256.digest("hex")).toArray
}
override def hash(msg1: Array[Byte], msg2: Array[Byte]*): Array[Byte] = {
hash(msg1 ++ msg2.flatten)
}
}
}

View File

@ -0,0 +1,83 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package fluence.crypto
import cats.data.EitherT
import cats.instances.try_._
import fluence.crypto.algorithm.{ CryptoErr, EcdsaJS }
import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpec }
import scodec.bits.ByteVector
import scala.util.{ Random, Try }
class EcdsaJSSpec extends WordSpec with Matchers with BeforeAndAfterAll {
def rndBytes(size: Int) = Random.nextString(10).getBytes
def rndByteVector(size: Int) = ByteVector(rndBytes(size))
private implicit class TryEitherTExtractor[A, B](et: EitherT[Try, A, B]) {
def extract: B = et.value.get.right.get
def isOk: Boolean = et.value.fold(_ false, _.isRight)
}
"ecdsa algorithm" should {
"correct sign and verify data" in {
val algorithm = EcdsaJS.ecdsa_secp256k1_sha256
val keys = algorithm.generateKeyPair[Try]().extract
val data = rndByteVector(10)
val sign = algorithm.sign[Try](keys, data).extract
algorithm.verify[Try](sign, data).isOk shouldBe true
val randomData = rndByteVector(10)
val randomSign = algorithm.sign(keys, randomData).extract
algorithm.verify(sign.copy(sign = randomSign.sign), data).isOk shouldBe false
algorithm.verify(sign, randomData).isOk shouldBe false
}
"correctly work with signer and checker" in {
val algo = new SignAlgo(EcdsaJS.ecdsa_secp256k1_sha256)
val keys = algo.generateKeyPair().extract
val signer = algo.signer(keys)
val data = rndByteVector(10)
val sign = signer.sign(data).extract
algo.checker.check(sign, data).isOk shouldBe true
val randomSign = signer.sign(rndByteVector(10)).extract
algo.checker.check(randomSign, data).isOk shouldBe false
}
"throw an errors on invalid data" in {
val algo = new SignAlgo(EcdsaJS.ecdsa_secp256k1_sha256)
val keys = algo.generateKeyPair().extract
val signer = algo.signer(keys)
val data = rndByteVector(10)
val sign = signer.sign(data).extract
the[CryptoErr] thrownBy algo.checker.check(sign.copy(sign = rndByteVector(10)), data).value.flatMap(_.toTry).get
the[CryptoErr] thrownBy algo.checker.check(sign.copy(publicKey = sign.publicKey.copy(value = rndByteVector(10))), data).value.flatMap(_.toTry).get
}
}
}

View File

@ -26,13 +26,9 @@ import cats.syntax.functor._
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
import io.circe.parser.decode import io.circe.parser.decode
import io.circe.syntax._ import io.circe.syntax._
import io.circe.{ Decoder, Encoder, HCursor, Json }
import scodec.bits.ByteVector
import scala.language.higherKinds import scala.language.higherKinds
case class KeyStore(keyPair: KeyPair)
class FileKeyStorage[F[_]](file: File)(implicit F: MonadError[F, Throwable]) { class FileKeyStorage[F[_]](file: File)(implicit F: MonadError[F, Throwable]) {
import KeyStore._ import KeyStore._
def readKeyPair: F[KeyPair] = { def readKeyPair: F[KeyPair] = {
@ -64,24 +60,3 @@ class FileKeyStorage[F[_]](file: File)(implicit F: MonadError[F, Throwable]) {
} yield newKeys } yield newKeys
} }
} }
object KeyStore {
implicit val encodeKeyStorage: Encoder[KeyStore] = new Encoder[KeyStore] {
final def apply(ks: KeyStore): Json = Json.obj(("keystore", Json.obj(
("secret", Json.fromString(ks.keyPair.secretKey.value.toBase64)),
("public", Json.fromString(ks.keyPair.publicKey.value.toBase64)))))
}
implicit val decodeKeyStorage: Decoder[Option[KeyStore]] = new Decoder[Option[KeyStore]] {
final def apply(c: HCursor): Decoder.Result[Option[KeyStore]] =
for {
secret c.downField("keystore").downField("secret").as[String]
public c.downField("keystore").downField("public").as[String]
} yield {
for {
secret ByteVector.fromBase64(secret)
public ByteVector.fromBase64(public)
} yield KeyStore(KeyPair.fromByteVectors(public, secret))
}
}
}

View File

@ -22,66 +22,73 @@ import java.security._
import java.security.interfaces.ECPrivateKey import java.security.interfaces.ECPrivateKey
import cats.data.EitherT import cats.data.EitherT
import cats.{ Applicative, Monad, MonadError } import cats.Monad
import cats.syntax.flatMap._ import fluence.crypto.hash.{ CryptoHasher, JdkCryptoHasher }
import cats.syntax.functor._
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECPublicKey import org.bouncycastle.jce.interfaces.ECPublicKey
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.{ ECNamedCurveParameterSpec, ECParameterSpec, ECPrivateKeySpec, ECPublicKeySpec } import org.bouncycastle.jce.spec.{ ECParameterSpec, ECPrivateKeySpec, ECPublicKeySpec }
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.language.higherKinds import scala.language.higherKinds
import scala.util.control.NonFatal
/** /**
* Elliptic Curve Digital Signature Algorithm * Elliptic Curve Digital Signature Algorithm
* @param curveType http://www.bouncycastle.org/wiki/display/JA1/Supported+Curves+%28ECDSA+and+ECGOST%29 * @param curveType http://www.bouncycastle.org/wiki/display/JA1/Supported+Curves+%28ECDSA+and+ECGOST%29
* @param scheme https://bouncycastle.org/specifications.html * @param scheme https://bouncycastle.org/specifications.html
*/ */
class Ecdsa(curveType: String, scheme: String) extends JavaAlgorithm class Ecdsa(curveType: String, scheme: String, hasher: Option[CryptoHasher[Array[Byte], Array[Byte]]]) extends JavaAlgorithm
with SignatureFunctions with KeyGenerator { with SignatureFunctions with KeyGenerator {
import CryptoErr._
import Ecdsa._ import Ecdsa._
private def nonFatalHandling[F[_] : Applicative, A](a: A)(errorText: String): EitherT[F, CryptoErr, A] = { val HEXradix = 16
try EitherT.pure(a)
catch {
case NonFatal(e) EitherT.leftT(CryptoErr(errorText + " " + e.getLocalizedMessage))
}
}
override def generateKeyPair[F[_] : Monad](random: SecureRandom): EitherT[F, CryptoErr, KeyPair] = override def generateKeyPair[F[_] : Monad](seed: Option[Array[Byte]]): EitherT[F, CryptoErr, KeyPair] = {
for { for {
ecSpec EitherT.fromOption[F]( ecSpec EitherT.fromOption(Option(ECNamedCurveTable.getParameterSpec(curveType)), CryptoErr("Parameter spec for the curve is not available."))
Option(ECNamedCurveTable.getParameterSpec(curveType)),
CryptoErr("Parameter spec for the curve is not available."))
g getKeyPairGenerator g getKeyPairGenerator
_ nonFatalHandling(g.initialize(ecSpec, random))("Could not initialize KeyPairGenerator.") _ nonFatalHandling(g.initialize(ecSpec, seed.map(new SecureRandom(_)).getOrElse(new SecureRandom())))("Could not initialize KeyPairGenerator.")
p EitherT.fromOption( p EitherT.fromOption(
Option(g.generateKeyPair()), Option(g.generateKeyPair()),
CryptoErr("Could not generate KeyPair. Unexpected.")) CryptoErr("Could not generate KeyPair. Unexpected."))
keyPair nonFatalHandling { keyPair nonFatalHandling {
//store S number for private key and compressed Q point on curve for public key //store S number for private key and compressed Q point on curve for public key
val pk = p.getPublic.asInstanceOf[ECPublicKey].getQ.getEncoded(true) val pk = ByteVector(p.getPublic.asInstanceOf[ECPublicKey].getQ.getEncoded(true))
val sk = p.getPrivate.asInstanceOf[ECPrivateKey].getS.toByteArray val bg = p.getPrivate.asInstanceOf[ECPrivateKey].getS
KeyPair.fromBytes(pk, sk) val sk = ByteVector.fromValidHex(bg.toString(HEXradix))
}("Can't get public/private key from generated keypair") KeyPair.fromByteVectors(pk, sk)
}("Could not generate KeyPair. Unexpected.")
} yield keyPair } yield keyPair
}
override def generateKeyPair[F[_] : Monad](): EitherT[F, CryptoErr, KeyPair] = override def sign[F[_] : Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, fluence.crypto.signature.Signature] = {
generateKeyPair(new SecureRandom()) signMessage(new BigInteger(keyPair.secretKey.value.toHex, HEXradix), message.toArray)
override def sign[F[_] : Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, fluence.crypto.signature.Signature] =
signMessage(keyPair.secretKey.value.toArray, message.toArray)
.map(bb fluence.crypto.signature.Signature(keyPair.publicKey, ByteVector(bb))) .map(bb fluence.crypto.signature.Signature(keyPair.publicKey, ByteVector(bb)))
}
override def verify[F[_] : Monad](signature: fluence.crypto.signature.Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] = { override def verify[F[_] : Monad](signature: fluence.crypto.signature.Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] = {
val publicKey = signature.publicKey.value.toArray verifySign(signature.publicKey.value.toArray, message.toArray, signature.sign.toArray)
val messageBytes = message.toArray }
val signatureBytes = signature.sign.toArray
private def signMessage[F[_] : Monad](privateKey: BigInteger, message: Array[Byte]): EitherT[F, CryptoErr, Array[Byte]] = {
for {
ec curveSpec
keySpec nonFatalHandling(new ECPrivateKeySpec(privateKey, ec))("Cannot read private key.")
keyFactory getKeyFactory
signProvider getSignatureProvider
sign {
nonFatalHandling {
signProvider.initSign(keyFactory.generatePrivate(keySpec))
signProvider.update(hasher.map(h h.hash(message)).getOrElse(message))
signProvider.sign()
}("Cannot sign message.")
}
} yield sign
}
private def verifySign[F[_] : Monad](publicKey: Array[Byte], message: Array[Byte], signature: Array[Byte]): EitherT[F, CryptoErr, Unit] = {
for { for {
ec curveSpec 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.")
@ -90,30 +97,14 @@ class Ecdsa(curveType: String, scheme: String) extends JavaAlgorithm
verify { verify {
nonFatalHandling { nonFatalHandling {
signProvider.initVerify(keyFactory.generatePublic(keySpec)) signProvider.initVerify(keyFactory.generatePublic(keySpec))
signProvider.update(messageBytes) signProvider.update(hasher.map(h h.hash(message)).getOrElse(message))
signProvider.verify(signatureBytes) signProvider.verify(signature)
}("Cannot verify message.") }("Cannot verify message.")
} }
_ EitherT.cond[F](verify, (), CryptoErr("Signature is not verified")) _ EitherT.cond[F](verify, (), CryptoErr("Signature is not verified"))
} yield () } yield ()
} }
private def signMessage[F[_] : Monad](privateKey: Array[Byte], message: Array[Byte]): EitherT[F, CryptoErr, Array[Byte]] = {
for {
ec curveSpec
keySpec nonFatalHandling(new ECPrivateKeySpec(new BigInteger(privateKey), ec))("Cannot read private key.")
keyFactory getKeyFactory
signProvider getSignatureProvider
sign {
nonFatalHandling {
signProvider.initSign(keyFactory.generatePrivate(keySpec))
signProvider.update(message)
signProvider.sign()
}("Cannot sign message.")
}
} yield sign
}
private def curveSpec[F[_] : Monad] = private def curveSpec[F[_] : Monad] =
nonFatalHandling(ECNamedCurveTable.getParameterSpec(curveType).asInstanceOf[ECParameterSpec])("Cannot get curve parameters.") nonFatalHandling(ECNamedCurveTable.getParameterSpec(curveType).asInstanceOf[ECParameterSpec])("Cannot get curve parameters.")
@ -134,7 +125,8 @@ object Ecdsa {
/** /**
* size of key is 256 bit * size of key is 256 bit
* `secp256k1` refers to the parameters of the ECDSA curve * `secp256k1` refers to the parameters of the ECDSA curve
* `SHA256withECDSA` Preferably the size of the key is greater than or equal to the digest algorithm * `NONEwithECDSA with sha-256 hasher` Preferably the size of the key is greater than or equal to the digest algorithm
* don't use `SHA256WithECDSA` because of non-compatibility with javascript libraries
*/ */
lazy val ecdsa_secp256k1_sha256 = new Ecdsa("secp256k1", "SHA256withECDSA") def ecdsa_secp256k1_sha256 = new Ecdsa("secp256k1", "NONEwithECDSA", Some(JdkCryptoHasher.Sha256))
} }

View File

@ -0,0 +1,57 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package fluence.crypto
import fluence.crypto.keypair.KeyPair
import io.circe.{ Decoder, Encoder, HCursor, Json }
import scodec.bits.{ Bases, ByteVector }
import scala.language.higherKinds
case class KeyStore(keyPair: KeyPair)
/**
* Json example:
* {
* "keystore" : {
* "secret" : "SFcDtZClfcxx75w9xJpQgBm09d6h9tVmVUEgHYxlews=",
* "public" : "AlTBivFrIYe++9Me4gr4R11BtRzjZ2WXZGDNWD/bEPka"
* }
* }
*/
object KeyStore {
private val alphabet = Bases.Alphabets.Base64Url
implicit val encodeKeyStorage: Encoder[KeyStore] = new Encoder[KeyStore] {
final def apply(ks: KeyStore): Json = Json.obj(("keystore", Json.obj(
("secret", Json.fromString(ks.keyPair.secretKey.value.toBase64(alphabet))),
("public", Json.fromString(ks.keyPair.publicKey.value.toBase64(alphabet))))))
}
implicit val decodeKeyStorage: Decoder[Option[KeyStore]] = new Decoder[Option[KeyStore]] {
final def apply(c: HCursor): Decoder.Result[Option[KeyStore]] =
for {
secret c.downField("keystore").downField("secret").as[String]
public c.downField("keystore").downField("public").as[String]
} yield {
for {
secret ByteVector.fromBase64(secret, alphabet)
public ByteVector.fromBase64(public, alphabet)
} yield KeyStore(KeyPair.fromByteVectors(public, secret))
}
}
}

View File

@ -17,10 +17,8 @@
package fluence.crypto package fluence.crypto
import java.security.SecureRandom import cats.Monad
import cats.data.EitherT import cats.data.EitherT
import cats.{ Monad, MonadError }
import fluence.crypto.algorithm.{ CryptoErr, DumbSign, KeyGenerator, SignatureFunctions } import fluence.crypto.algorithm.{ CryptoErr, DumbSign, KeyGenerator, SignatureFunctions }
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.{ Signature, SignatureChecker, Signer } import fluence.crypto.signature.{ Signature, SignatureChecker, Signer }
@ -34,12 +32,12 @@ import scala.language.higherKinds
*/ */
class SignAlgo(algo: KeyGenerator with SignatureFunctions) { class SignAlgo(algo: KeyGenerator with SignatureFunctions) {
def generateKeyPair[F[_] : Monad](): EitherT[F, CryptoErr, KeyPair] = algo.generateKeyPair() def generateKeyPair[F[_] : Monad](seed: Option[ByteVector] = None): EitherT[F, CryptoErr, KeyPair] =
def generateKeyPair[F[_] : Monad](seed: ByteVector): EitherT[F, CryptoErr, KeyPair] = algo.generateKeyPair(new SecureRandom(seed.toArray)) algo.generateKeyPair(seed.map(_.toArray))
def signer(kp: KeyPair): Signer = new Signer { def signer(kp: KeyPair): Signer = new Signer {
override def publicKey: KeyPair.Public = kp.publicKey
override def sign[F[_] : Monad](plain: ByteVector): EitherT[F, CryptoErr, Signature] = algo.sign(kp, plain) override def sign[F[_] : Monad](plain: ByteVector): EitherT[F, CryptoErr, Signature] = algo.sign(kp, plain)
override def publicKey: KeyPair.Public = kp.publicKey
} }
def checker: SignatureChecker = new SignatureChecker { def checker: SignatureChecker = new SignatureChecker {

View File

@ -17,6 +17,19 @@
package fluence.crypto.algorithm package fluence.crypto.algorithm
import scala.util.control.NoStackTrace import cats.Applicative
import cats.data.EitherT
import scala.language.higherKinds
import scala.util.control.{ NoStackTrace, NonFatal }
case class CryptoErr(errorMessage: String) extends Throwable(errorMessage) with NoStackTrace case class CryptoErr(errorMessage: String) extends Throwable(errorMessage) with NoStackTrace
object CryptoErr {
def nonFatalHandling[F[_] : Applicative, A](a: A)(errorText: String): EitherT[F, CryptoErr, A] = {
try EitherT.pure(a)
catch {
case NonFatal(e) EitherT.leftT(CryptoErr(errorText + " " + e.getLocalizedMessage))
}
}
}

View File

@ -14,7 +14,6 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package fluence.crypto.algorithm package fluence.crypto.algorithm
import java.security.SecureRandom import java.security.SecureRandom
@ -28,11 +27,11 @@ import scodec.bits.ByteVector
import scala.language.higherKinds import scala.language.higherKinds
class DumbSign extends KeyGenerator with SignatureFunctions { class DumbSign extends KeyGenerator with SignatureFunctions {
override def generateKeyPair[F[_] : Monad](random: SecureRandom): EitherT[F, CryptoErr, KeyPair] =
EitherT.pure(KeyPair.fromBytes(random.generateSeed(10), random.generateSeed(10)))
override def generateKeyPair[F[_] : Monad](): EitherT[F, CryptoErr, KeyPair] = override def generateKeyPair[F[_] : Monad](seed: Option[Array[Byte]] = None): EitherT[F, CryptoErr, KeyPair] = {
generateKeyPair(new SecureRandom()) val s = seed.getOrElse(new SecureRandom().generateSeed(10))
EitherT.pure(KeyPair.fromBytes(s, s))
}
override def sign[F[_] : Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, Signature] = override def sign[F[_] : Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, Signature] =
EitherT.pure(Signature(keyPair.publicKey, message.reverse)) EitherT.pure(Signature(keyPair.publicKey, message.reverse))

View File

@ -17,8 +17,6 @@
package fluence.crypto.algorithm package fluence.crypto.algorithm
import java.security.SecureRandom
import cats.Monad import cats.Monad
import cats.data.EitherT import cats.data.EitherT
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
@ -26,7 +24,5 @@ import fluence.crypto.keypair.KeyPair
import scala.language.higherKinds import scala.language.higherKinds
trait KeyGenerator { trait KeyGenerator {
def generateKeyPair[F[_] : Monad](random: SecureRandom): EitherT[F, CryptoErr, KeyPair] def generateKeyPair[F[_] : Monad](seed: Option[Array[Byte]] = None): EitherT[F, CryptoErr, KeyPair]
def generateKeyPair[F[_] : Monad](): EitherT[F, CryptoErr, KeyPair]
} }