refactor sign classes (#34)

* 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
This commit is contained in:
Dima 2018-02-09 16:11:03 +03:00 committed by Dmitry Kurinskiy
parent fdae5eaf8f
commit 79738aa2c7
10 changed files with 135 additions and 81 deletions

View File

@ -17,17 +17,18 @@
package fluence.crypto.algorithm package fluence.crypto.algorithm
import java.math.BigInteger
import java.security._ import java.security._
import java.security.spec.{ PKCS8EncodedKeySpec, X509EncodedKeySpec } import java.security.interfaces.ECPrivateKey
import cats.MonadError import cats.MonadError
import cats.syntax.flatMap._ import cats.syntax.flatMap._
import cats.syntax.functor._ import cats.syntax.functor._
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.SignatureChecker
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECPublicKey
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec import org.bouncycastle.jce.spec.{ ECNamedCurveParameterSpec, ECParameterSpec, ECPrivateKeySpec, ECPublicKeySpec }
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.language.higherKinds import scala.language.higherKinds
@ -38,18 +39,17 @@ import scala.util.control.NonFatal
* @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[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, Throwable]) extends JavaAlgorithm class Ecdsa(curveType: String, scheme: String) extends JavaAlgorithm
with SignatureFunctions[F] with KeyGenerator[F] { with SignatureFunctions with KeyGenerator {
import Ecdsa._ import Ecdsa._
private def nonFatalHandling[A](a: A)(errorText: String): F[A] = { private def nonFatalHandling[F[_], A](a: A)(errorText: String)(implicit F: MonadError[F, Throwable]): F[A] = {
try F.pure(a) try F.pure(a)
catch { catch {
case NonFatal(e) F.raiseError(CryptoErr(errorText + " " + e.getLocalizedMessage)) case NonFatal(e) F.raiseError(CryptoErr(errorText + " " + e.getLocalizedMessage))
} }
} }
override def generateKeyPair(random: SecureRandom): F[KeyPair] = { override def generateKeyPair[F[_]](random: SecureRandom)(implicit F: MonadError[F, Throwable]): F[KeyPair] = {
import JavaAlgorithm._
for { for {
ecSpecOp F.pure(Option(ECNamedCurveTable.getParameterSpec(curveType))) ecSpecOp F.pure(Option(ECNamedCurveTable.getParameterSpec(curveType)))
ecSpec ecSpecOp match { ecSpec ecSpecOp match {
@ -59,28 +59,33 @@ class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, T
g getKeyPairGenerator g getKeyPairGenerator
_ nonFatalHandling(g.initialize(ecSpec, random))("Could not initialize KeyPairGenerator.") _ nonFatalHandling(g.initialize(ecSpec, random))("Could not initialize KeyPairGenerator.")
keyPair Option(g.generateKeyPair()) match { keyPair Option(g.generateKeyPair()) match {
case Some(p) F.pure(p) case Some(p)
case None F.raiseError[java.security.KeyPair](CryptoErr("Could not generate KeyPair. Unexpected.")) //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 sk = p.getPrivate.asInstanceOf[ECPrivateKey].getS.toByteArray
F.pure(KeyPair.fromBytes(pk, sk))
case None F.raiseError[KeyPair](CryptoErr("Could not generate KeyPair. Unexpected."))
} }
} yield keyPair } yield keyPair
} }
override def generateKeyPair(): F[KeyPair] = { override def generateKeyPair[F[_]]()(implicit F: MonadError[F, Throwable]): F[KeyPair] = {
generateKeyPair(new SecureRandom()) generateKeyPair(new SecureRandom())
} }
override def sign(keyPair: KeyPair, message: ByteVector): F[fluence.crypto.signature.Signature] = { override def sign[F[_]](keyPair: KeyPair, message: ByteVector)(implicit F: MonadError[F, Throwable]): F[fluence.crypto.signature.Signature] = {
signMessage(keyPair.secretKey.value.toArray, message.toArray) 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(signature: fluence.crypto.signature.Signature, message: ByteVector): F[Boolean] = { override def verify[F[_]](signature: fluence.crypto.signature.Signature, message: ByteVector)(implicit F: MonadError[F, Throwable]): F[Boolean] = {
verifySign(signature.publicKey.value.toArray, message.toArray, signature.sign.toArray) verifySign(signature.publicKey.value.toArray, message.toArray, signature.sign.toArray)
} }
private def signMessage(privateKey: Array[Byte], message: Array[Byte]): F[Array[Byte]] = { private def signMessage[F[_]](privateKey: Array[Byte], message: Array[Byte])(implicit F: MonadError[F, Throwable]): F[Array[Byte]] = {
val keySpec = new PKCS8EncodedKeySpec(privateKey)
for { for {
ec curveSpec
keySpec nonFatalHandling(new ECPrivateKeySpec(new BigInteger(privateKey), ec))("Cannot read private key.")
keyFactory getKeyFactory keyFactory getKeyFactory
signProvider getSignatureProvider signProvider getSignatureProvider
sign { sign {
@ -93,9 +98,10 @@ class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, T
} yield sign } yield sign
} }
private def verifySign(publicKey: Array[Byte], message: Array[Byte], signature: Array[Byte]): F[Boolean] = { private def verifySign[F[_]](publicKey: Array[Byte], message: Array[Byte], signature: Array[Byte])(implicit F: MonadError[F, Throwable]): F[Boolean] = {
val keySpec = new X509EncodedKeySpec(publicKey)
for { for {
ec curveSpec
keySpec nonFatalHandling(new ECPublicKeySpec(ec.getCurve.decodePoint(publicKey), ec))("Cannot read public key.")
keyFactory getKeyFactory keyFactory getKeyFactory
signProvider getSignatureProvider signProvider getSignatureProvider
verify { verify {
@ -108,13 +114,16 @@ class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, T
} yield verify } yield verify
} }
private lazy val getKeyPairGenerator = private def curveSpec[F[_]](implicit F: MonadError[F, Throwable]) =
nonFatalHandling(ECNamedCurveTable.getParameterSpec(curveType).asInstanceOf[ECParameterSpec])("Cannot get curve parameters.")
private def getKeyPairGenerator[F[_]](implicit F: MonadError[F, Throwable]) =
nonFatalHandling(KeyPairGenerator.getInstance(ECDSA, BouncyCastleProvider.PROVIDER_NAME))("Cannot get key pair generator.") nonFatalHandling(KeyPairGenerator.getInstance(ECDSA, BouncyCastleProvider.PROVIDER_NAME))("Cannot get key pair generator.")
private lazy val getKeyFactory = private def getKeyFactory[F[_]](implicit F: MonadError[F, Throwable]) =
nonFatalHandling(KeyFactory.getInstance(ECDSA, BouncyCastleProvider.PROVIDER_NAME))("Cannot get key factory instance.") nonFatalHandling(KeyFactory.getInstance(ECDSA, BouncyCastleProvider.PROVIDER_NAME))("Cannot get key factory instance.")
private lazy val getSignatureProvider = private def getSignatureProvider[F[_]](implicit F: MonadError[F, Throwable]) =
nonFatalHandling(Signature.getInstance(scheme, BouncyCastleProvider.PROVIDER_NAME))("Cannot get signature instance.") nonFatalHandling(Signature.getInstance(scheme, BouncyCastleProvider.PROVIDER_NAME))("Cannot get signature instance.")
} }
@ -128,16 +137,4 @@ object Ecdsa {
* `SHA256withECDSA` Preferably the size of the key is greater than or equal to the digest algorithm * `SHA256withECDSA` Preferably the size of the key is greater than or equal to the digest algorithm
*/ */
def ecdsa_secp256k1_sha256[F[_]](implicit F: MonadError[F, Throwable]) = new Ecdsa("secp256k1", "SHA256withECDSA") def ecdsa_secp256k1_sha256[F[_]](implicit F: MonadError[F, Throwable]) = new Ecdsa("secp256k1", "SHA256withECDSA")
class Signer(keyPair: KeyPair) extends fluence.crypto.signature.Signer {
override def publicKey: KeyPair.Public = keyPair.publicKey
override def sign[F[_]](plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[fluence.crypto.signature.Signature] =
Ecdsa.ecdsa_secp256k1_sha256.sign(keyPair, plain)
}
case object Checker extends SignatureChecker {
override def check[F[_]](signature: fluence.crypto.signature.Signature, plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[Boolean] =
Ecdsa.ecdsa_secp256k1_sha256.verify(signature, plain)
}
} }

View File

@ -19,9 +19,7 @@ package fluence.crypto.algorithm
import java.security.Security import java.security.Security
import fluence.crypto.keypair.KeyPair
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import scodec.bits.ByteVector
import scala.language.{ higherKinds, implicitConversions } import scala.language.{ higherKinds, implicitConversions }
@ -33,15 +31,6 @@ private[crypto] trait JavaAlgorithm extends Algorithm {
} }
object JavaAlgorithm { object JavaAlgorithm {
/**
* getEncoded methods return ASN.1 encoding in PKCS#8 standard for secret key and X.509 standard for public
* ASN.1 serialization standard defined by ISO. https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
* PKCS #8 is a standard syntax for storing private key information. https://en.wikipedia.org/wiki/PKCS_8
* X.509 is a standard that defines the format of public key certificates. https://en.wikipedia.org/wiki/X.509
*/
implicit def jKeyPairToKeyPair(jKeyPair: java.security.KeyPair): KeyPair =
KeyPair(KeyPair.Public(ByteVector(jKeyPair.getPublic.getEncoded)), KeyPair.Secret(ByteVector(jKeyPair.getPrivate.getEncoded)))
/** /**
* add JVM-specific security provider in class loader * add JVM-specific security provider in class loader
*/ */

View File

@ -52,37 +52,34 @@ class SignatureSpec extends WordSpec with Matchers with BeforeAndAfterAll {
} }
"correctly work with signer and checker" in { "correctly work with signer and checker" in {
val keys = Ecdsa.ecdsa_secp256k1_sha256.generateKeyPair().get val algo = new SignAlgo(Ecdsa.ecdsa_secp256k1_sha256)
val signer = new Ecdsa.Signer(keys) val keys = algo.generateKeyPair().get
val signer = algo.signer(keys)
val data = rndByteVector(10) val data = rndByteVector(10)
val sign = signer.sign(data).get val sign = signer.sign(data).get
Ecdsa.Checker.check(sign, data).get shouldBe true algo.checker.check(sign, data).get shouldBe true
val randomSign = signer.sign(rndByteVector(10)).get val randomSign = signer.sign(rndByteVector(10)).get
Ecdsa.Checker.check(randomSign, data).get shouldBe false algo.checker.check(randomSign, data).get shouldBe false
} }
"throw an errors on invalid data" in { "throw an errors on invalid data" in {
val keys = Ecdsa.ecdsa_secp256k1_sha256.generateKeyPair().get val algo = new SignAlgo(Ecdsa.ecdsa_secp256k1_sha256)
val signer = new Ecdsa.Signer(keys) val keys = algo.generateKeyPair().get
val signer = algo.signer(keys)
val data = rndByteVector(10) val data = rndByteVector(10)
val brokenSecret = keys.copy(secretKey = KeyPair.Secret(rndByteVector(10)))
val brokenSigner = new Ecdsa.Signer(brokenSecret)
the[CryptoErr] thrownBy brokenSigner.sign(data).get
val sign = signer.sign(data).get val sign = signer.sign(data).get
the[CryptoErr] thrownBy Ecdsa.Checker.check(sign.copy(sign = rndByteVector(10)), data).get the[CryptoErr] thrownBy algo.checker.check(sign.copy(sign = rndByteVector(10)), data).get
the[CryptoErr] thrownBy Ecdsa.Checker.check(sign.copy(publicKey = sign.publicKey.copy(value = rndByteVector(10))), data).get the[CryptoErr] thrownBy algo.checker.check(sign.copy(publicKey = sign.publicKey.copy(value = rndByteVector(10))), data).get
} }
"store and read key from file" in { "store and read key from file" in {
val seed = rndBytes(32) val algo = new SignAlgo(Ecdsa.ecdsa_secp256k1_sha256)
val keys = Ecdsa.ecdsa_secp256k1_sha256.generateKeyPair(new SecureRandom(seed)).get val keys = algo.generateKeyPair().get
val keyFile = File.createTempFile("test", "") val keyFile = File.createTempFile("test", "")
if (keyFile.exists()) keyFile.delete() if (keyFile.exists()) keyFile.delete()
@ -93,12 +90,12 @@ class SignatureSpec extends WordSpec with Matchers with BeforeAndAfterAll {
val keysReadE = storage.readKeyPair val keysReadE = storage.readKeyPair
val keysRead = keysReadE.get val keysRead = keysReadE.get
val signer = new Ecdsa.Signer(keys) val signer = algo.signer(keys)
val data = rndByteVector(10) val data = rndByteVector(10)
val sign = signer.sign(data).get val sign = signer.sign(data).get
Ecdsa.Checker.check(sign.copy(publicKey = keysRead.publicKey), data).get shouldBe true algo.checker.check(sign.copy(publicKey = keysRead.publicKey), data).get shouldBe true
Ecdsa.Checker.check(sign, data).get shouldBe true algo.checker.check(sign, data).get shouldBe true
//try to store key into previously created file //try to store key into previously created file
storage.storeSecretKey(keys).isFailure shouldBe true storage.storeSecretKey(keys).isFailure shouldBe true

View File

@ -29,6 +29,8 @@ import io.circe.syntax._
import io.circe.{ Decoder, Encoder, HCursor, Json } import io.circe.{ Decoder, Encoder, HCursor, Json }
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.language.higherKinds
case class KeyStore(keyPair: KeyPair) 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]) {

View File

@ -0,0 +1,49 @@
/*
* 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 java.security.SecureRandom
import cats.MonadError
import fluence.crypto.algorithm.{ DumbSign, KeyGenerator, SignatureFunctions }
import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.{ Signature, SignatureChecker, Signer }
import scodec.bits.ByteVector
import scala.language.higherKinds
/**
* Class for generation keys, signers and checkers
* @param algo implementation of sign alghoritms, e.g. ECDSA
*/
class SignAlgo(algo: KeyGenerator with SignatureFunctions) {
def generateKeyPair[F[_]]()(implicit F: MonadError[F, Throwable]): F[KeyPair] = algo.generateKeyPair()
def generateKeyPair[F[_]](seed: ByteVector)(implicit F: MonadError[F, Throwable]): F[KeyPair] = algo.generateKeyPair(new SecureRandom(seed.toArray))
def signer[F[_]](kp: KeyPair)(implicit F: MonadError[F, Throwable]): Signer[F] = new Signer[F] {
override def sign(plain: ByteVector): F[Signature] = algo.sign(kp, plain)
override def publicKey: KeyPair.Public = kp.publicKey
}
def checker[F[_]](implicit F: MonadError[F, Throwable]): SignatureChecker[F] = (signature: Signature, plain: ByteVector) algo.verify(signature, plain)
}
object SignAlgo {
val dumb = new SignAlgo(new DumbSign())
}

View File

@ -0,0 +1,24 @@
package fluence.crypto.algorithm
import java.security.SecureRandom
import cats.MonadError
import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.Signature
import scodec.bits.ByteVector
import scala.language.higherKinds
class DumbSign extends KeyGenerator with SignatureFunctions {
override def generateKeyPair[F[_]](random: SecureRandom)(implicit F: MonadError[F, Throwable]): F[KeyPair] =
F.pure(KeyPair.fromBytes(random.generateSeed(10), random.generateSeed(10)))
override def generateKeyPair[F[_]]()(implicit F: MonadError[F, Throwable]): F[KeyPair] =
generateKeyPair(new SecureRandom())
override def sign[F[_]](keyPair: KeyPair, message: ByteVector)(implicit F: MonadError[F, Throwable]): F[Signature] =
F.pure(Signature(keyPair.publicKey, message.reverse))
override def verify[F[_]](signature: Signature, message: ByteVector)(implicit F: MonadError[F, Throwable]): F[Boolean] =
F.pure(signature.sign == message.reverse)
}

View File

@ -19,11 +19,12 @@ package fluence.crypto.algorithm
import java.security.SecureRandom import java.security.SecureRandom
import cats.MonadError
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
import scala.language.higherKinds import scala.language.higherKinds
trait KeyGenerator[F[_]] { trait KeyGenerator {
def generateKeyPair(random: SecureRandom): F[KeyPair] def generateKeyPair[F[_]](random: SecureRandom)(implicit F: MonadError[F, Throwable]): F[KeyPair]
def generateKeyPair(): F[KeyPair] def generateKeyPair[F[_]]()(implicit F: MonadError[F, Throwable]): F[KeyPair]
} }

View File

@ -17,11 +17,14 @@
package fluence.crypto.algorithm package fluence.crypto.algorithm
import cats.MonadError
import fluence.crypto.keypair.KeyPair import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.Signature import fluence.crypto.signature.Signature
import scodec.bits.ByteVector import scodec.bits.ByteVector
trait SignatureFunctions[F[_]] { import scala.language.higherKinds
def sign(keyPair: KeyPair, message: ByteVector): F[Signature]
def verify(signature: Signature, message: ByteVector): F[Boolean] trait SignatureFunctions {
def sign[F[_]](keyPair: KeyPair, message: ByteVector)(implicit F: MonadError[F, Throwable]): F[Signature]
def verify[F[_]](signature: Signature, message: ByteVector)(implicit F: MonadError[F, Throwable]): F[Boolean]
} }

View File

@ -17,18 +17,10 @@
package fluence.crypto.signature package fluence.crypto.signature
import cats.MonadError
import scodec.bits.ByteVector import scodec.bits.ByteVector
import scala.language.higherKinds import scala.language.higherKinds
trait SignatureChecker { trait SignatureChecker[F[_]] {
def check[F[_]](signature: Signature, plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[Boolean] def check(signature: Signature, plain: ByteVector): F[Boolean]
}
object SignatureChecker {
case object DumbChecker extends SignatureChecker {
override def check[F[_]](signature: Signature, plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[Boolean] =
F.pure(signature.sign == plain.reverse)
}
} }

View File

@ -23,17 +23,17 @@ import scodec.bits.ByteVector
import scala.language.higherKinds import scala.language.higherKinds
trait Signer { trait Signer[F[_]] {
def publicKey: KeyPair.Public def publicKey: KeyPair.Public
def sign[F[_]](plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[Signature] def sign(plain: ByteVector): F[Signature]
} }
object Signer { object Signer {
class DumbSigner(keyPair: KeyPair) extends Signer { class DumbSigner[F[_]](keyPair: KeyPair)(implicit F: MonadError[F, Throwable]) extends Signer[F] {
override def publicKey: KeyPair.Public = keyPair.publicKey override def publicKey: KeyPair.Public = keyPair.publicKey
override def sign[F[_]](plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[Signature] = override def sign(plain: ByteVector): F[Signature] =
F.pure(Signature(keyPair.publicKey, plain.reverse)) F.pure(Signature(keyPair.publicKey, plain.reverse))
} }
} }