From 994194b31f60136704ec32e49734fd7f95a4b7c3 Mon Sep 17 00:00:00 2001 From: Constantine Solovev Date: Mon, 2 Apr 2018 10:38:25 +0400 Subject: [PATCH] Crypto api changes (#102) * remove pubKey from Signature, change crypto api * refactoring dependent modules --- .../fluence/crypto/algorithm/Ecdsa.scala | 12 +++++--- .../test/scala/fluence/crypto/EcdsaSpec.scala | 16 ++++++----- .../fluence/crypto/algorithm/Ecdsa.scala | 7 +++-- .../scala/fluence/crypto/SignatureSpec.scala | 18 ++++++------ src/main/scala/fluence/crypto/SignAlgo.scala | 4 +-- .../fluence/crypto/algorithm/DumbSign.scala | 4 +-- .../crypto/algorithm/SignatureFunctions.scala | 2 +- .../fluence/crypto/keypair/KeyPair.scala | 10 +++++-- .../crypto/signature/PubKeyAndSignature.scala | 28 +++++++++++++++++++ .../fluence/crypto/signature/Signature.scala | 15 ++++++++-- .../crypto/signature/SignatureChecker.scala | 2 +- .../fluence/crypto/signature/Signer.scala | 2 +- 12 files changed, 86 insertions(+), 34 deletions(-) create mode 100644 src/main/scala/fluence/crypto/signature/PubKeyAndSignature.scala diff --git a/js/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala b/js/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala index c581488..da5e0af 100644 --- a/js/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala +++ b/js/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala @@ -17,8 +17,8 @@ package fluence.crypto.algorithm -import cats.data.EitherT import cats.Monad +import cats.data.EitherT import fluence.crypto.SignAlgo import fluence.crypto.facade.ecdsa.EC import fluence.crypto.hash.{CryptoHasher, JsCryptoHasher} @@ -58,7 +58,7 @@ class Ecdsa(ec: EC, hasher: Option[CryptoHasher[Array[Byte], Array[Byte]]]) }("Cannot get private key from key pair.") hash ← hash(message) signHex ← nonFatalHandling(secret.sign(new Uint8Array(hash)).toDER("hex"))("Cannot sign message") - } yield Signature(keyPair.publicKey, ByteVector.fromValidHex(signHex)) + } yield Signature(ByteVector.fromValidHex(signHex)) } def hash[F[_]: Monad](message: ByteVector): EitherT[F, CryptoErr, js.Array[Byte]] = { @@ -72,10 +72,14 @@ class Ecdsa(ec: EC, hasher: Option[CryptoHasher[Array[Byte], Array[Byte]]]) .map(_.toJSArray) } - override def verify[F[_]: Monad](signature: Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] = { + override def verify[F[_]: Monad]( + pubKey: KeyPair.Public, + signature: Signature, + message: ByteVector + ): EitherT[F, CryptoErr, Unit] = { for { public ← nonFatalHandling { - val hex = signature.publicKey.value.toHex + val hex = pubKey.value.toHex ec.keyFromPublic(hex, "hex") }("Incorrect public key format.") hash ← hash(message) diff --git a/js/src/test/scala/fluence/crypto/EcdsaSpec.scala b/js/src/test/scala/fluence/crypto/EcdsaSpec.scala index d866c12..4610b65 100644 --- a/js/src/test/scala/fluence/crypto/EcdsaSpec.scala +++ b/js/src/test/scala/fluence/crypto/EcdsaSpec.scala @@ -21,6 +21,7 @@ import cats.data.EitherT import cats.instances.try_._ import fluence.crypto.algorithm.{CryptoErr, Ecdsa} import fluence.crypto.keypair.KeyPair +import fluence.crypto.signature.Signature import org.scalatest.{Matchers, WordSpec} import scodec.bits.ByteVector @@ -48,17 +49,18 @@ class EcdsaSpec extends WordSpec with Matchers { val algorithm = Ecdsa.ecdsa_secp256k1_sha256 val keys = algorithm.generateKeyPair[Try]().extract + val pubKey = keys.publicKey val data = rndByteVector(10) val sign = algorithm.sign[Try](keys, data).extract - algorithm.verify[Try](sign, data).isOk shouldBe true + algorithm.verify[Try](pubKey, 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(pubKey, randomSign, data).isOk shouldBe false - algorithm.verify(sign, randomData).isOk shouldBe false + algorithm.verify(pubKey, sign, randomData).isOk shouldBe false } "correctly work with signer and checker" in { @@ -70,10 +72,10 @@ class EcdsaSpec extends WordSpec with Matchers { val data = rndByteVector(10) val sign = signer.sign(data).extract - checker.check(sign.sign, data).isOk shouldBe true + checker.check(sign, data).isOk shouldBe true val randomSign = signer.sign(rndByteVector(10)).extract - checker.check(randomSign.sign, data).isOk shouldBe false + checker.check(randomSign, data).isOk shouldBe false } "throw an errors on invalid data" in { @@ -85,10 +87,10 @@ class EcdsaSpec extends WordSpec with Matchers { val sign = signer.sign(data).extract - the[CryptoErr] thrownBy checker.check(rndByteVector(10), data).value.flatMap(_.toTry).get + the[CryptoErr] thrownBy checker.check(Signature(rndByteVector(10)), data).value.flatMap(_.toTry).get val invalidChecker = algo.checker(KeyPair.fromByteVectors(rndByteVector(10), rndByteVector(10)).publicKey) the[CryptoErr] thrownBy invalidChecker - .check(sign.sign, data) + .check(sign, data) .value .flatMap(_.toTry) .get diff --git a/jvm/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala b/jvm/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala index 436ef59..295eb4f 100644 --- a/jvm/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala +++ b/jvm/src/main/scala/fluence/crypto/algorithm/Ecdsa.scala @@ -72,14 +72,15 @@ class Ecdsa(curveType: String, scheme: String, hasher: Option[CryptoHasher[Array message: ByteVector ): EitherT[F, CryptoErr, fluence.crypto.signature.Signature] = { signMessage(new BigInteger(keyPair.secretKey.value.toHex, HEXradix), message.toArray) - .map(bb ⇒ fluence.crypto.signature.Signature(keyPair.publicKey, ByteVector(bb))) + .map(bb ⇒ fluence.crypto.signature.Signature(ByteVector(bb))) } override def verify[F[_]: Monad]( + publicKey: KeyPair.Public, signature: fluence.crypto.signature.Signature, message: ByteVector ): EitherT[F, CryptoErr, Unit] = { - verifySign(signature.publicKey.value.toArray, message.toArray, signature.sign.toArray) + verifySign(publicKey.bytes, signature.bytes, message.toArray) } private def signMessage[F[_]: Monad]( @@ -103,8 +104,8 @@ class Ecdsa(curveType: String, scheme: String, hasher: Option[CryptoHasher[Array private def verifySign[F[_]: Monad]( publicKey: Array[Byte], + signature: Array[Byte], message: Array[Byte], - signature: Array[Byte] ): EitherT[F, CryptoErr, Unit] = { for { ec ← curveSpec diff --git a/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala b/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala index a34e5e6..3999d82 100644 --- a/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala +++ b/jvm/src/test/scala/fluence/crypto/SignatureSpec.scala @@ -23,6 +23,7 @@ import cats.data.EitherT import cats.instances.try_._ import fluence.crypto.algorithm.{CryptoErr, Ecdsa} import fluence.crypto.keypair.KeyPair +import fluence.crypto.signature.Signature import org.scalatest.{Matchers, WordSpec} import scodec.bits.ByteVector @@ -50,17 +51,18 @@ class SignatureSpec extends WordSpec with Matchers { val algorithm = Ecdsa.ecdsa_secp256k1_sha256 val keys = algorithm.generateKeyPair[Try]().extract + val pubKey = keys.publicKey val data = rndByteVector(10) val sign = algorithm.sign[Try](keys, data).extract - algorithm.verify[Try](sign, data).isOk shouldBe true + algorithm.verify[Try](pubKey, 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(pubKey, randomSign, data).isOk shouldBe false - algorithm.verify(sign, randomData).isOk shouldBe false + algorithm.verify(pubKey, sign, randomData).isOk shouldBe false } "correctly work with signer and checker" in { @@ -70,11 +72,11 @@ class SignatureSpec extends WordSpec with Matchers { val checker = algo.checker(keys.publicKey) val data = rndByteVector(10) - val sign = signer.sign(data).extract.sign + val sign = signer.sign(data).extract checker.check(sign, data).isOk shouldBe true - val randomSign = signer.sign(rndByteVector(10)).extract.sign + val randomSign = signer.sign(rndByteVector(10)).extract checker.check(randomSign, data).isOk shouldBe false } @@ -85,10 +87,10 @@ class SignatureSpec extends WordSpec with Matchers { val checker = algo.checker(keys.publicKey) val data = rndByteVector(10) - val sign = signer.sign(data).extract.sign + val sign = signer.sign(data).extract the[CryptoErr] thrownBy { - checker.check(rndByteVector(10), data).value.flatMap(_.toTry).get + checker.check(Signature(rndByteVector(10)), data).value.flatMap(_.toTry).get } val invalidChecker = algo.checker(KeyPair.fromByteVectors(rndByteVector(10), rndByteVector(10)).publicKey) the[CryptoErr] thrownBy { @@ -115,7 +117,7 @@ class SignatureSpec extends WordSpec with Matchers { val signer = algo.signer(keys) val data = rndByteVector(10) - val sign = signer.sign(data).extract.sign + val sign = signer.sign(data).extract algo.checker(keys.publicKey).check(sign, data).isOk shouldBe true algo.checker(keysRead.publicKey).check(sign, data).isOk shouldBe true diff --git a/src/main/scala/fluence/crypto/SignAlgo.scala b/src/main/scala/fluence/crypto/SignAlgo.scala index a8c3728..0ba0b42 100644 --- a/src/main/scala/fluence/crypto/SignAlgo.scala +++ b/src/main/scala/fluence/crypto/SignAlgo.scala @@ -55,8 +55,8 @@ class SignAlgo(name: String, algo: KeyGenerator with SignatureFunctions) { * @return */ def checker(publicKey: KeyPair.Public): SignatureChecker = new SignatureChecker { - override def check[F[_]: Monad](signature: ByteVector, plain: ByteVector): EitherT[F, CryptoErr, Unit] = - algo.verify(Signature(publicKey, signature), plain) + override def check[F[_]: Monad](signature: Signature, plain: ByteVector): EitherT[F, CryptoErr, Unit] = + algo.verify(publicKey, signature, plain) override def toString: String = s"SignatureChecker($name)" } diff --git a/src/main/scala/fluence/crypto/algorithm/DumbSign.scala b/src/main/scala/fluence/crypto/algorithm/DumbSign.scala index 3671b62..86c9b44 100644 --- a/src/main/scala/fluence/crypto/algorithm/DumbSign.scala +++ b/src/main/scala/fluence/crypto/algorithm/DumbSign.scala @@ -35,8 +35,8 @@ class DumbSign extends KeyGenerator with SignatureFunctions { } override def sign[F[_]: Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, Signature] = - EitherT.pure(Signature(keyPair.publicKey, message.reverse)) + EitherT.pure(Signature(message.reverse)) - override def verify[F[_]: Monad](signature: Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] = + override def verify[F[_]: Monad](pubKey: KeyPair.Public, signature: Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] = EitherT.cond[F](signature.sign == message.reverse, (), CryptoErr("Invalid Signature")) } diff --git a/src/main/scala/fluence/crypto/algorithm/SignatureFunctions.scala b/src/main/scala/fluence/crypto/algorithm/SignatureFunctions.scala index 35fc951..7e890fa 100644 --- a/src/main/scala/fluence/crypto/algorithm/SignatureFunctions.scala +++ b/src/main/scala/fluence/crypto/algorithm/SignatureFunctions.scala @@ -28,5 +28,5 @@ import scala.language.higherKinds trait SignatureFunctions { def sign[F[_]: Monad](keyPair: KeyPair, message: ByteVector): EitherT[F, CryptoErr, Signature] - def verify[F[_]: Monad](signature: Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] + def verify[F[_]: Monad](pubKey: KeyPair.Public, signature: Signature, message: ByteVector): EitherT[F, CryptoErr, Unit] } diff --git a/src/main/scala/fluence/crypto/keypair/KeyPair.scala b/src/main/scala/fluence/crypto/keypair/KeyPair.scala index 1deba6a..b738b5d 100644 --- a/src/main/scala/fluence/crypto/keypair/KeyPair.scala +++ b/src/main/scala/fluence/crypto/keypair/KeyPair.scala @@ -22,8 +22,14 @@ import scodec.bits.ByteVector case class KeyPair(publicKey: KeyPair.Public, secretKey: KeyPair.Secret) object KeyPair { - case class Public(value: ByteVector) extends AnyVal - case class Secret(value: ByteVector) extends AnyVal + + case class Public(value: ByteVector) extends AnyVal { + def bytes: Array[Byte] = value.toArray + } + + case class Secret(value: ByteVector) extends AnyVal { + def bytes: Array[Byte] = value.toArray + } def fromBytes(pk: Array[Byte], sk: Array[Byte]): KeyPair = fromByteVectors(ByteVector(pk), ByteVector(sk)) def fromByteVectors(pk: ByteVector, sk: ByteVector): KeyPair = KeyPair(Public(pk), Secret(sk)) diff --git a/src/main/scala/fluence/crypto/signature/PubKeyAndSignature.scala b/src/main/scala/fluence/crypto/signature/PubKeyAndSignature.scala new file mode 100644 index 0000000..f7243e5 --- /dev/null +++ b/src/main/scala/fluence/crypto/signature/PubKeyAndSignature.scala @@ -0,0 +1,28 @@ +/* + * 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.signature + +import fluence.crypto.keypair.KeyPair + +/** + * Container for public key of signer and a signature. + * + * @param publicKey Public key of signature maker + * @param signature Some signature + */ +case class PubKeyAndSignature(publicKey: KeyPair.Public, signature: Signature) diff --git a/src/main/scala/fluence/crypto/signature/Signature.scala b/src/main/scala/fluence/crypto/signature/Signature.scala index 559deee..af76f18 100644 --- a/src/main/scala/fluence/crypto/signature/Signature.scala +++ b/src/main/scala/fluence/crypto/signature/Signature.scala @@ -17,8 +17,17 @@ package fluence.crypto.signature -import fluence.crypto.keypair.KeyPair import scodec.bits.ByteVector -// todo remove PubKey from signature -case class Signature(publicKey: KeyPair.Public, sign: ByteVector) +/** Container for a signature. */ +case class Signature private (sign: ByteVector) extends AnyVal { + def bytes: Array[Byte] = sign.toArray +} + +object Signature { + + def apply(sign: ByteVector): Signature = new Signature(sign) + + def apply(sign: Array[Byte]): Signature = new Signature(ByteVector(sign)) + +} diff --git a/src/main/scala/fluence/crypto/signature/SignatureChecker.scala b/src/main/scala/fluence/crypto/signature/SignatureChecker.scala index 60fb2cd..53971a8 100644 --- a/src/main/scala/fluence/crypto/signature/SignatureChecker.scala +++ b/src/main/scala/fluence/crypto/signature/SignatureChecker.scala @@ -25,5 +25,5 @@ import scodec.bits.ByteVector import scala.language.higherKinds trait SignatureChecker { - def check[F[_]: Monad](signature: ByteVector, plain: ByteVector): EitherT[F, CryptoErr, Unit] + def check[F[_]: Monad](signature: Signature, plain: ByteVector): EitherT[F, CryptoErr, Unit] } diff --git a/src/main/scala/fluence/crypto/signature/Signer.scala b/src/main/scala/fluence/crypto/signature/Signer.scala index c64b043..e04b53e 100644 --- a/src/main/scala/fluence/crypto/signature/Signer.scala +++ b/src/main/scala/fluence/crypto/signature/Signer.scala @@ -40,7 +40,7 @@ object Signer { override def publicKey: KeyPair.Public = keyPair.publicKey override def sign[F[_]: Monad](plain: ByteVector): EitherT[F, CryptoErr, Signature] = - EitherT.pure(Signature(keyPair.publicKey, plain.reverse)) + EitherT.pure(Signature(plain.reverse)) } }