mirror of
https://github.com/fluencelabs/crypto
synced 2025-04-24 14:22:18 +00:00
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:
parent
fdae5eaf8f
commit
79738aa2c7
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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]) {
|
||||
|
49
src/main/scala/fluence/crypto/SignAlgo.scala
Normal file
49
src/main/scala/fluence/crypto/SignAlgo.scala
Normal 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())
|
||||
}
|
24
src/main/scala/fluence/crypto/algorithm/DumbSign.scala
Normal file
24
src/main/scala/fluence/crypto/algorithm/DumbSign.scala
Normal 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)
|
||||
}
|
@ -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]
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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]
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user