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
import java.math.BigInteger
import java.security._
import java.security.spec.{ PKCS8EncodedKeySpec, X509EncodedKeySpec }
import java.security.interfaces.ECPrivateKey
import cats.MonadError
import cats.syntax.flatMap._
import cats.syntax.functor._
import fluence.crypto.keypair.KeyPair
import fluence.crypto.signature.SignatureChecker
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECPublicKey
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 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 scheme https://bouncycastle.org/specifications.html
*/
class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, Throwable]) extends JavaAlgorithm
with SignatureFunctions[F] with KeyGenerator[F] {
class Ecdsa(curveType: String, scheme: String) extends JavaAlgorithm
with SignatureFunctions with KeyGenerator {
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)
catch {
case NonFatal(e) F.raiseError(CryptoErr(errorText + " " + e.getLocalizedMessage))
}
}
override def generateKeyPair(random: SecureRandom): F[KeyPair] = {
import JavaAlgorithm._
override def generateKeyPair[F[_]](random: SecureRandom)(implicit F: MonadError[F, Throwable]): F[KeyPair] = {
for {
ecSpecOp F.pure(Option(ECNamedCurveTable.getParameterSpec(curveType)))
ecSpec ecSpecOp match {
@ -59,28 +59,33 @@ class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, T
g getKeyPairGenerator
_ nonFatalHandling(g.initialize(ecSpec, random))("Could not initialize KeyPairGenerator.")
keyPair Option(g.generateKeyPair()) match {
case Some(p) F.pure(p)
case None F.raiseError[java.security.KeyPair](CryptoErr("Could not generate KeyPair. Unexpected."))
case Some(p)
//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
}
override def generateKeyPair(): F[KeyPair] = {
override def generateKeyPair[F[_]]()(implicit F: MonadError[F, Throwable]): F[KeyPair] = {
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)
.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)
}
private def signMessage(privateKey: Array[Byte], message: Array[Byte]): F[Array[Byte]] = {
val keySpec = new PKCS8EncodedKeySpec(privateKey)
private def signMessage[F[_]](privateKey: Array[Byte], message: Array[Byte])(implicit F: MonadError[F, Throwable]): F[Array[Byte]] = {
for {
ec curveSpec
keySpec nonFatalHandling(new ECPrivateKeySpec(new BigInteger(privateKey), ec))("Cannot read private key.")
keyFactory getKeyFactory
signProvider getSignatureProvider
sign {
@ -93,9 +98,10 @@ class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, T
} yield sign
}
private def verifySign(publicKey: Array[Byte], message: Array[Byte], signature: Array[Byte]): F[Boolean] = {
val keySpec = new X509EncodedKeySpec(publicKey)
private def verifySign[F[_]](publicKey: Array[Byte], message: Array[Byte], signature: Array[Byte])(implicit F: MonadError[F, Throwable]): F[Boolean] = {
for {
ec curveSpec
keySpec nonFatalHandling(new ECPublicKeySpec(ec.getCurve.decodePoint(publicKey), ec))("Cannot read public key.")
keyFactory getKeyFactory
signProvider getSignatureProvider
verify {
@ -108,13 +114,16 @@ class Ecdsa[F[_]](curveType: String, scheme: String)(implicit F: MonadError[F, T
} 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.")
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.")
private lazy val getSignatureProvider =
private def getSignatureProvider[F[_]](implicit F: MonadError[F, Throwable]) =
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
*/
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 fluence.crypto.keypair.KeyPair
import org.bouncycastle.jce.provider.BouncyCastleProvider
import scodec.bits.ByteVector
import scala.language.{ higherKinds, implicitConversions }
@ -33,15 +31,6 @@ private[crypto] trait JavaAlgorithm extends Algorithm {
}
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
*/

View File

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

View File

@ -29,6 +29,8 @@ import io.circe.syntax._
import io.circe.{ Decoder, Encoder, HCursor, Json }
import scodec.bits.ByteVector
import scala.language.higherKinds
case class KeyStore(keyPair: KeyPair)
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 cats.MonadError
import fluence.crypto.keypair.KeyPair
import scala.language.higherKinds
trait KeyGenerator[F[_]] {
def generateKeyPair(random: SecureRandom): F[KeyPair]
def generateKeyPair(): F[KeyPair]
trait KeyGenerator {
def generateKeyPair[F[_]](random: SecureRandom)(implicit F: MonadError[F, Throwable]): F[KeyPair]
def generateKeyPair[F[_]]()(implicit F: MonadError[F, Throwable]): F[KeyPair]
}

View File

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

View File

@ -23,17 +23,17 @@ import scodec.bits.ByteVector
import scala.language.higherKinds
trait Signer {
trait Signer[F[_]] {
def publicKey: KeyPair.Public
def sign[F[_]](plain: ByteVector)(implicit F: MonadError[F, Throwable]): F[Signature]
def sign(plain: ByteVector): F[Signature]
}
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 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))
}
}