WIP cleanup but working well
All checks were successful
/ Build and Test Library Snapshot (pull_request) Successful in 2m25s
All checks were successful
/ Build and Test Library Snapshot (pull_request) Successful in 2m25s
This commit is contained in:
parent
f4bbd53d44
commit
fae965fa32
10 changed files with 165 additions and 45 deletions
|
|
@ -33,11 +33,12 @@ val Deps = new {
|
|||
}
|
||||
|
||||
val Gs = new {
|
||||
val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.1.1"
|
||||
val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.4.1"
|
||||
}
|
||||
|
||||
val BouncyCastle = new {
|
||||
val Provider: ModuleID = "org.bouncycastle" % "bcprov-jdk18on" % "1.83"
|
||||
val PKIX: ModuleID = "org.bouncycastle" % "bcpkix-jdk18on" % "1.83"
|
||||
}
|
||||
|
||||
val MUnit: ModuleID = "org.scalameta" %% "munit" % "1.2.2"
|
||||
|
|
@ -65,7 +66,8 @@ lazy val core = project
|
|||
)
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.Cats.Effect
|
||||
Deps.Cats.Effect,
|
||||
Deps.BouncyCastle.PKIX
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,39 @@
|
|||
package gs.crypto.v0
|
||||
|
||||
import cats.effect.Sync
|
||||
import cats.syntax.all.*
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
|
||||
/** Base implementation for [[KeyLoader]] that provides standard parsing
|
||||
* functionality.
|
||||
*/
|
||||
abstract class BaseKeyLoader[F[_]] extends KeyLoader[F]:
|
||||
/** @return
|
||||
* The key loader configuration.
|
||||
*/
|
||||
abstract class BaseKeyLoader[F[_]: Sync] extends KeyLoader[F]:
|
||||
|
||||
def loadPublicKey(
|
||||
location: String,
|
||||
algorithm: String
|
||||
): F[Either[KeyLoadError, PublicKey]] =
|
||||
loadKey(location).flatMap {
|
||||
case Left(err) => Sync[F].pure(Left(err))
|
||||
case Right(key) =>
|
||||
KeyLoader.loadPublicKey[F](key, algorithm).map(Right(_))
|
||||
}
|
||||
|
||||
def loadPrivateKey(
|
||||
location: String,
|
||||
algorithm: String
|
||||
): F[Either[KeyLoadError, PrivateKey]] =
|
||||
loadKey(location).flatMap {
|
||||
case Left(err) => Sync[F].pure(Left(err))
|
||||
case Right(key) =>
|
||||
KeyLoader.loadPrivateKey[F](key, algorithm).map(Right(_))
|
||||
}
|
||||
|
||||
/** @return
|
||||
* The key loader configuration.
|
||||
*/
|
||||
|
||||
def config: BaseKeyLoader.Config
|
||||
|
||||
protected def prepareKey(base: String): Either[KeyLoadError, Array[Byte]] =
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package gs.crypto.v0
|
|||
|
||||
import cats.effect.Sync
|
||||
import cats.syntax.all.*
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
|
|
@ -14,8 +13,7 @@ import java.nio.file.Path
|
|||
* The expected character set for the loaded file.
|
||||
*/
|
||||
class FileKeyLoader[F[_]: Sync](
|
||||
val config: BaseKeyLoader.Config,
|
||||
val charset: Charset
|
||||
val config: BaseKeyLoader.Config
|
||||
) extends BaseKeyLoader[F]:
|
||||
|
||||
/** @inheritDocs
|
||||
|
|
@ -28,5 +26,5 @@ class FileKeyLoader[F[_]: Sync](
|
|||
case false =>
|
||||
Sync[F].pure(Left(KeyLoadError.PathNotRegularFile(p, location)))
|
||||
case true =>
|
||||
Sync[F].delay(Files.readString(p, charset)).map(prepareKey)
|
||||
Sync[F].delay(Files.readString(p)).map(prepareKey)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,61 @@
|
|||
package gs.crypto.v0
|
||||
|
||||
import cats.Applicative
|
||||
import cats.effect.Sync
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.KeyFactory
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
|
||||
/** Interface for loading keys (e.g. public/private keys) into memory.
|
||||
*/
|
||||
trait KeyLoader[F[_]]:
|
||||
/** Load the key from the given location.
|
||||
/** Load the key from the given location. This gets the raw bytes, but does
|
||||
* not handle _any_ standard encodings such as PKCS8 or X509. These are
|
||||
* handled by helper functions (static) or [[BaseKeyLoader]].
|
||||
*
|
||||
* @param location
|
||||
* The location of the key.
|
||||
* @return
|
||||
* The loaded key.
|
||||
* The key's raw bytes - ready for further decoding.
|
||||
*/
|
||||
def loadKey(location: String): F[Either[KeyLoadError, Array[Byte]]]
|
||||
|
||||
object KeyLoader:
|
||||
|
||||
def stringLoader[F[_]: Applicative](
|
||||
config: BaseKeyLoader.Config
|
||||
def stringLoader[F[_]: Sync](
|
||||
config: BaseKeyLoader.Config = BaseKeyLoader.Defaults.Config
|
||||
): StringKeyLoader[F] =
|
||||
new StringKeyLoader[F](config)
|
||||
new StringKeyLoader(config)
|
||||
|
||||
def fileLoader[F[_]: Sync](
|
||||
config: BaseKeyLoader.Config,
|
||||
charset: Charset = StandardCharsets.UTF_8
|
||||
config: BaseKeyLoader.Config = BaseKeyLoader.Defaults.Config
|
||||
): FileKeyLoader[F] =
|
||||
new FileKeyLoader(config, charset)
|
||||
new FileKeyLoader(config)
|
||||
|
||||
def resourceLoader[F[_]: Sync](
|
||||
config: BaseKeyLoader.Config
|
||||
config: BaseKeyLoader.Config = BaseKeyLoader.Defaults.Config
|
||||
): ResourceKeyLoader[F] =
|
||||
new ResourceKeyLoader(config)
|
||||
|
||||
def loadPublicKey[F[_]: Sync](
|
||||
publicKeyRawBytes: Array[Byte],
|
||||
algorithm: String
|
||||
): F[PublicKey] =
|
||||
Sync[F].delay {
|
||||
val spec = new X509EncodedKeySpec(publicKeyRawBytes)
|
||||
val keyFactory = KeyFactory.getInstance(algorithm)
|
||||
keyFactory.generatePublic(spec)
|
||||
}
|
||||
|
||||
def loadPrivateKey[F[_]: Sync](
|
||||
privateKeyRawBytes: Array[Byte],
|
||||
algorithm: String
|
||||
): F[PrivateKey] =
|
||||
Sync[F].delay {
|
||||
val spec = new PKCS8EncodedKeySpec(privateKeyRawBytes)
|
||||
val keyFactory = KeyFactory.getInstance(algorithm)
|
||||
keyFactory.generatePrivate(spec)
|
||||
}
|
||||
|
||||
end KeyLoader
|
||||
|
|
|
|||
|
|
@ -3,4 +3,8 @@ package gs.crypto.v0
|
|||
/** Used to verify cryptographic signatures.
|
||||
*/
|
||||
trait SignatureVerifier[F[_]]:
|
||||
def verify(signature: Signature): F[SignatureValidity]
|
||||
|
||||
def verify(
|
||||
signature: Signature,
|
||||
data: Array[Byte]
|
||||
): F[SignatureValidity]
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
package gs.crypto.v0
|
||||
|
||||
import cats.Applicative
|
||||
import cats.effect.Sync
|
||||
|
||||
/** Implementation of [[KeyLoader]] that loads keys directly from input strings.
|
||||
*
|
||||
* @param config
|
||||
* The key loader configuration.
|
||||
*/
|
||||
class StringKeyLoader[F[_]: Applicative](
|
||||
class StringKeyLoader[F[_]: Sync](
|
||||
val config: BaseKeyLoader.Config
|
||||
) extends BaseKeyLoader[F]:
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def loadKey(
|
||||
location: String
|
||||
): F[Either[KeyLoadError, Array[Byte]]] =
|
||||
Applicative[F].pure(prepareKey(location))
|
||||
override def loadKey(location: String): F[Either[KeyLoadError, Array[Byte]]] =
|
||||
Sync[F].delay(prepareKey(location))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package gs.crypto.v0.eddsa
|
||||
|
||||
object Ed25519:
|
||||
|
||||
val Algorithm: String = "Ed25519"
|
||||
|
||||
end Ed25519
|
||||
|
|
@ -5,7 +5,7 @@ import gs.crypto.v0.Signature
|
|||
import gs.crypto.v0.Signer
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
|
||||
import java.security.PrivateKey
|
||||
|
||||
/** Implementation of [[Signer]] that uses the Ed25519 system to calculate
|
||||
* signatures.
|
||||
|
|
@ -16,18 +16,17 @@ import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
|
|||
* The private key.
|
||||
*/
|
||||
final class Ed25519Signer[F[_]: Sync](
|
||||
privateKey: Array[Byte]
|
||||
privateKey: PrivateKey
|
||||
) extends Signer[F]:
|
||||
private lazy val params = new Ed25519PrivateKeyParameters(privateKey)
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def sign(data: Array[Byte]): F[Signature] =
|
||||
Sync[F].delay {
|
||||
val s = new org.bouncycastle.crypto.signers.Ed25519Signer()
|
||||
val _ = s.init(true, params)
|
||||
val _ = s.update(data, 0, data.length)
|
||||
Signature(s.generateSignature())
|
||||
val s = java.security.Signature.getInstance(Ed25519.Algorithm)
|
||||
val _ = s.initSign(privateKey)
|
||||
val _ = s.update(data)
|
||||
Signature(s.sign())
|
||||
}
|
||||
|
||||
/** @inheritDocs
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import cats.effect.Sync
|
|||
import gs.crypto.v0.Signature
|
||||
import gs.crypto.v0.SignatureValidity
|
||||
import gs.crypto.v0.SignatureVerifier
|
||||
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters
|
||||
import java.security.PublicKey
|
||||
|
||||
/** Implementation of [[SignatureVerifier]] that uses the Ed25519 system to
|
||||
* verify signatures.
|
||||
|
|
@ -15,15 +15,18 @@ import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters
|
|||
* The public key.
|
||||
*/
|
||||
final class Ed25519Verifier[F[_]: Sync](
|
||||
publicKey: Array[Byte]
|
||||
publicKey: PublicKey
|
||||
) extends SignatureVerifier[F]:
|
||||
private lazy val params = new Ed25519PublicKeyParameters(publicKey)
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def verify(signature: Signature): F[SignatureValidity] =
|
||||
override def verify(
|
||||
signature: Signature,
|
||||
data: Array[Byte] // TODO: CLEAN UP
|
||||
): F[SignatureValidity] =
|
||||
Sync[F].delay {
|
||||
val s = new org.bouncycastle.crypto.signers.Ed25519Signer()
|
||||
val _ = s.init(false, params)
|
||||
SignatureValidity(s.verifySignature(signature.unwrap()))
|
||||
val s = java.security.Signature.getInstance(Ed25519.Algorithm)
|
||||
val _ = s.initVerify(publicKey)
|
||||
val _ = s.update(data)
|
||||
SignatureValidity(s.verify(signature.unwrap()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package gs.crypto.v0.eddsa
|
||||
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.IORuntime
|
||||
import gs.crypto.v0.KeyLoader
|
||||
import gs.crypto.v0.SignatureValidity
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.util.UUID
|
||||
import munit.Location
|
||||
|
||||
class Ed25519Tests extends munit.FunSuite:
|
||||
import Ed25519Tests.Resources
|
||||
|
||||
given IORuntime = IORuntime.global
|
||||
|
||||
def iotest(
|
||||
name: String
|
||||
)(
|
||||
f: => IO[Unit]
|
||||
)(
|
||||
using
|
||||
Location
|
||||
): Unit =
|
||||
test(name)(f.unsafeRunSync())
|
||||
|
||||
val keyLoader = KeyLoader.resourceLoader[IO]()
|
||||
|
||||
iotest(
|
||||
"should sign some data and verify that signature"
|
||||
) {
|
||||
val data = UUID.randomUUID().toString()
|
||||
loadKeysOrFail().flatMap { case (publicKey, privateKey) =>
|
||||
val signer = new Ed25519Signer[IO](privateKey)
|
||||
val verifier = new Ed25519Verifier[IO](publicKey)
|
||||
|
||||
for
|
||||
signature <- signer.sign(data)
|
||||
validity <- verifier.verify(signature, data.getBytes())
|
||||
yield assertEquals(validity, SignatureValidity.Valid)
|
||||
}
|
||||
}
|
||||
|
||||
private def loadKeysOrFail(): IO[(PublicKey, PrivateKey)] =
|
||||
keyLoader.loadPublicKey(Resources.PublicKey, Ed25519.Algorithm).flatMap {
|
||||
case Left(_) => fail("Failed to load public key.")
|
||||
case Right(pub) =>
|
||||
keyLoader.loadPrivateKey(Resources.PrivateKey, Ed25519.Algorithm).map {
|
||||
case Left(_) => fail("Failed to load private key.")
|
||||
case Right(priv) => (pub, priv)
|
||||
}
|
||||
}
|
||||
|
||||
object Ed25519Tests:
|
||||
|
||||
object Resources:
|
||||
val PublicKey: String = "public.pem"
|
||||
val PrivateKey: String = "private.pem"
|
||||
end Resources
|
||||
|
||||
end Ed25519Tests
|
||||
Loading…
Add table
Reference in a new issue