(patch) scalafmt
All checks were successful
/ Build and Test Library Snapshot (pull_request) Successful in 2m35s

This commit is contained in:
Pat Garrity 2026-02-15 19:54:22 -06:00
parent 503569c475
commit f4bbd53d44
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
14 changed files with 204 additions and 177 deletions

View file

@ -3,11 +3,11 @@ package gs.crypto.v0
import java.{util => ju} import java.{util => ju}
/** Implementation of [[Decoder]] for Base64 strings. /** Implementation of [[Decoder]] for Base64 strings.
* *
* Supports base64-url decoding as well. * Supports base64-url decoding as well.
*/ */
object Base64Decoder extends Decoder[B64]: object Base64Decoder extends Decoder[B64]:
private lazy val d: ju.Base64.Decoder = ju.Base64.getDecoder() private lazy val d: ju.Base64.Decoder = ju.Base64.getDecoder()
private lazy val du: ju.Base64.Decoder = ju.Base64.getUrlDecoder() private lazy val du: ju.Base64.Decoder = ju.Base64.getUrlDecoder()
/** @inheritDocs /** @inheritDocs
@ -25,11 +25,12 @@ object Base64Decoder extends Decoder[B64]:
def decodeUnsafe(input: String): Array[Byte] = def decodeUnsafe(input: String): Array[Byte] =
d.decode(input) d.decode(input)
/** /** Decode the base64-url encoded input.
* Decode the base64-url encoded input. *
* * @param input
* @param input The base64-url encoded data. * The base64-url encoded data.
* @return The decoded bytes. * @return
*/ * The decoded bytes.
*/
def decodeUrl(input: B64Url): Array[Byte] = def decodeUrl(input: B64Url): Array[Byte] =
du.decode(input.data) du.decode(input.data)

View file

@ -5,11 +5,11 @@ import java.nio.charset.StandardCharsets
import java.util.Base64 import java.util.Base64
/** Implementation of [[Encoder]] for Base64. /** Implementation of [[Encoder]] for Base64.
* *
* Supports base64-url encoding as well. * Supports base64-url encoding as well.
*/ */
object Base64Encoder extends Encoder[B64]: object Base64Encoder extends Encoder[B64]:
private lazy val e: Base64.Encoder = Base64.getEncoder() private lazy val e: Base64.Encoder = Base64.getEncoder()
private lazy val eu: Base64.Encoder = Base64.getUrlEncoder() private lazy val eu: Base64.Encoder = Base64.getUrlEncoder()
/** @inheritDocs /** @inheritDocs
@ -25,22 +25,25 @@ object Base64Encoder extends Encoder[B64]:
): B64 = ): B64 =
encode(input.getBytes(charset)) encode(input.getBytes(charset))
/** /** Encode the given bytes using base64-url.
* Encode the given bytes using base64-url. *
* * @param input
* @param input The input data. * The input data.
* @return The base64-url-encoded string. * @return
*/ * The base64-url-encoded string.
*/
def encodeUrl(input: Array[Byte]): B64Url = def encodeUrl(input: Array[Byte]): B64Url =
B64Url(eu.encodeToString(input)) B64Url(eu.encodeToString(input))
/** /** Encode the given bytes using base64-url.
* Encode the given bytes using base64-url. *
* * @param input
* @param input The input data. * The input data.
* @param charset The character set of the input data. * @param charset
* @return The base64-url-encoded string. * The character set of the input data.
*/ * @return
* The base64-url-encoded string.
*/
def encodeUrl( def encodeUrl(
input: String, input: String,
charset: Charset = StandardCharsets.UTF_8 charset: Charset = StandardCharsets.UTF_8

View file

@ -1,22 +1,30 @@
package gs.crypto.v0 package gs.crypto.v0
/** /** Base implementation for [[KeyLoader]] that provides standard parsing
* Base implementation for [[KeyLoader]] that provides standard parsing
* functionality. * functionality.
*/ */
abstract class BaseKeyLoader[F[_]] extends KeyLoader[F]: abstract class BaseKeyLoader[F[_]] extends KeyLoader[F]:
/** /** @return
* @return The key loader configuration. * The key loader configuration.
*/ */
def config: BaseKeyLoader.Config def config: BaseKeyLoader.Config
protected def prepareKey(base: String): Either[KeyLoadError, Array[Byte]] = protected def prepareKey(base: String): Either[KeyLoadError, Array[Byte]] =
scala.util.Try { scala.util
config.decoder.decode(trim(collapse(unwrap(base)))) .Try {
}.toEither.left.map(_ => KeyLoadError.DecodingFailure(config.encodingName)) config.decoder.decode(trim(collapse(unwrap(base))))
}
.toEither
.left
.map(_ => KeyLoadError.DecodingFailure(config.encodingName))
private def unwrap(base: String): String = private def unwrap(base: String): String =
config.wrappers.foldLeft(base) { (acc, w) => acc.replace(w, "") } config.wrappers.foldLeft(base) {
(
acc,
w
) => acc.replace(w, "")
}
private def collapse(base: String): String = private def collapse(base: String): String =
if config.shouldCollapse then base.replace("\n", "") else base if config.shouldCollapse then base.replace("\n", "") else base
@ -26,15 +34,19 @@ abstract class BaseKeyLoader[F[_]] extends KeyLoader[F]:
object BaseKeyLoader: object BaseKeyLoader:
/** /** Configuration for loading keys.
* Configuration for loading keys. *
* * @param wrappers
* @param wrappers Values that wrap keys and must be removed. * Values that wrap keys and must be removed.
* @param shouldCollapse Whether newlines should be collapsed. * @param shouldCollapse
* @param shouldTrim Whether the string data should be trimmed. * Whether newlines should be collapsed.
* @param encodingName The name of the encoding. * @param shouldTrim
* @param decoder The decoder to be used on the pre-processed input. * Whether the string data should be trimmed.
*/ * @param encodingName
* The name of the encoding.
* @param decoder
* The decoder to be used on the pre-processed input.
*/
case class Config( case class Config(
wrappers: List[String], wrappers: List[String],
shouldCollapse: Boolean, shouldCollapse: Boolean,
@ -43,39 +55,33 @@ object BaseKeyLoader:
decoder: Decoder[String] decoder: Decoder[String]
) )
/** /** Default configuration values that work in most cases.
* Default configuration values that work in most cases. */
*/
object Defaults: object Defaults:
/** /** By default, generic begin/end public/private key wrappers are included.
* By default, generic begin/end public/private key wrappers are included. */
*/
val Wrappers: List[String] = List( val Wrappers: List[String] = List(
"-----BEGIN PUBLIC KEY-----", "-----BEGIN PUBLIC KEY-----",
"-----END PUBLIC KEY-----", "-----END PUBLIC KEY-----",
"-----BEGIN PRIVATE KEY-----", "-----BEGIN PRIVATE KEY-----",
"-----END PRIVATE KEY-----", "-----END PRIVATE KEY-----"
) )
/** /** By default, stringified key data has newlines removed.
* By default, stringified key data has newlines removed. */
*/
val ShouldCollapse: Boolean = true val ShouldCollapse: Boolean = true
/** /** By default, stringified key data is trimmed.
* By default, stringified key data is trimmed. */
*/
val ShouldTrim: Boolean = true val ShouldTrim: Boolean = true
/** /** The default encoding name is 'base64'.
* The default encoding name is 'base64'. */
*/
val EncodingName: String = "base64" val EncodingName: String = "base64"
/** /** The default configuration handles standard wrapped, base64-encoded keys.
* The default configuration handles standard wrapped, base64-encoded keys. */
*/
val Config: BaseKeyLoader.Config = BaseKeyLoader.Config( val Config: BaseKeyLoader.Config = BaseKeyLoader.Config(
wrappers = Wrappers, wrappers = Wrappers,
shouldCollapse = ShouldCollapse, shouldCollapse = ShouldCollapse,

View file

@ -1,23 +1,25 @@
package gs.crypto.v0 package gs.crypto.v0
import cats.syntax.all.*
import cats.effect.Sync import cats.effect.Sync
import cats.syntax.all.*
import java.nio.charset.Charset
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.charset.Charset
/** /** Implementation of [[KeyLoader]] that loads keys from encoded string files.
* Implementation of [[KeyLoader]] that loads keys from encoded string files.
* *
* @param config The key loader configuration. * @param config
* @param charset The expected character set for the loaded file. * The key loader configuration.
* @param charset
* The expected character set for the loaded file.
*/ */
class FileKeyLoader[F[_]: Sync]( class FileKeyLoader[F[_]: Sync](
val config: BaseKeyLoader.Config, val config: BaseKeyLoader.Config,
val charset: Charset val charset: Charset
) extends BaseKeyLoader[F]: ) extends BaseKeyLoader[F]:
/** @inheritDocs */ /** @inheritDocs
*/
override def loadKey( override def loadKey(
location: String location: String
): F[Either[KeyLoadError, Array[Byte]]] = ): F[Either[KeyLoadError, Array[Byte]]] =

View file

@ -6,7 +6,11 @@ sealed trait KeyLoadError
object KeyLoadError: object KeyLoadError:
case class PathNotRegularFile(path: Path, location: String) extends KeyLoadError case class PathNotRegularFile(
path: Path,
location: String
) extends KeyLoadError
case class DecodingFailure(encodedFormat: String) extends KeyLoadError case class DecodingFailure(encodedFormat: String) extends KeyLoadError
end KeyLoadError end KeyLoadError

View file

@ -5,16 +5,16 @@ import cats.effect.Sync
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
/** /** Interface for loading keys (e.g. public/private keys) into memory.
* Interface for loading keys (e.g. public/private keys) into memory. */
*/
trait KeyLoader[F[_]]: trait KeyLoader[F[_]]:
/** /** Load the key from the given location.
* Load the key from the given location. *
* * @param location
* @param location The location of the key. * The location of the key.
* @return The loaded key. * @return
*/ * The loaded key.
*/
def loadKey(location: String): F[Either[KeyLoadError, Array[Byte]]] def loadKey(location: String): F[Either[KeyLoadError, Array[Byte]]]
object KeyLoader: object KeyLoader:

View file

@ -1,20 +1,21 @@
package gs.crypto.v0 package gs.crypto.v0
import cats.effect.Sync
import cats.effect.Resource import cats.effect.Resource
import cats.effect.Sync
import scala.io.Source import scala.io.Source
/** /** Implementation of [[KeyLoader]] that loads keys from encoded string files
* Implementation of [[KeyLoader]] that loads keys from encoded string files
* that exist as project resources. * that exist as project resources.
* *
* @param config The key loader configuration. * @param config
* The key loader configuration.
*/ */
class ResourceKeyLoader[F[_]: Sync]( class ResourceKeyLoader[F[_]: Sync](
val config: BaseKeyLoader.Config val config: BaseKeyLoader.Config
) extends BaseKeyLoader[F]: ) extends BaseKeyLoader[F]:
/** @inheritDocs */ /** @inheritDocs
*/
override def loadKey( override def loadKey(
location: String location: String
): F[Either[KeyLoadError, Array[Byte]]] = ): F[Either[KeyLoadError, Array[Byte]]] =
@ -22,6 +23,4 @@ class ResourceKeyLoader[F[_]: Sync](
.make(Sync[F].delay(Source.fromResource(location)))(source => .make(Sync[F].delay(Source.fromResource(location)))(source =>
Sync[F].delay(source.close()) Sync[F].delay(source.close())
) )
.use { source => .use(source => Sync[F].delay(prepareKey(source.getLines().mkString)))
Sync[F].delay(prepareKey(source.getLines().mkString))
}

View file

@ -1,50 +1,51 @@
package gs.crypto.v0 package gs.crypto.v0
/** /** Represents a cryptographic signature. Opaque type for an array of bytes.
* Represents a cryptographic signature. Opaque type for an array of bytes.
*/ */
opaque type Signature = Array[Byte] opaque type Signature = Array[Byte]
object Signature: object Signature:
/** /** Instantiate a new signature from the given byte array.
* Instantiate a new signature from the given byte array. *
* * @param value
* @param value The signature value. * The signature value.
* @return The signature bytes. * @return
*/ * The signature bytes.
*/
def apply(value: Array[Byte]): Signature = value def apply(value: Array[Byte]): Signature = value
/** /** Instantiate a new signature from the given encoded string.
* Instantiate a new signature from the given encoded string. *
* * @param value
* @param value The input data. * The input data.
* @return The new signature. * @return
*/ * The new signature.
*/
def fromEncoded(value: Encoded): Signature = def fromEncoded(value: Encoded): Signature =
value.decode() value.decode()
given CanEqual[Signature, Signature] = CanEqual.derived given CanEqual[Signature, Signature] = CanEqual.derived
extension (sig: Signature) extension (sig: Signature)
/** /** @return
* @return The underlying byte array. * The underlying byte array.
*/ */
def unwrap(): Array[Byte] = sig def unwrap(): Array[Byte] = sig
/** /** @return
* @return This signature, encoded using base64. * This signature, encoded using base64.
*/ */
def toBase64(): B64 = Base64Encoder.encode(sig) def toBase64(): B64 = Base64Encoder.encode(sig)
/** /** @return
* @return This signature, encoded using base64-url. * This signature, encoded using base64-url.
*/ */
def toBase64Url(): B64Url = Base64Encoder.encodeUrl(sig) def toBase64Url(): B64Url = Base64Encoder.encodeUrl(sig)
/** /** @return
* @return This signature, encoded using hex. * This signature, encoded using hex.
*/ */
def toHex(): Hex = HexEncoder.encode(sig) def toHex(): Hex = HexEncoder.encode(sig)
end Signature end Signature

View file

@ -1,47 +1,50 @@
package gs.crypto.v0 package gs.crypto.v0
/** /** Used to communicate the calculated validity of some [[Signature]].
* Used to communicate the calculated validity of some [[Signature]].
* *
* @param name The enumeration name. * @param name
* The enumeration name.
*/ */
sealed abstract class SignatureValidity(val name: String): sealed abstract class SignatureValidity(val name: String):
/** @inheritDocs */
/** @inheritDocs
*/
override def equals(obj: Any): Boolean = override def equals(obj: Any): Boolean =
obj match obj match
case other: SignatureValidity => name == other.name case other: SignatureValidity => name == other.name
case _ => false case _ => false
/** @inheritDocs */ /** @inheritDocs
*/
override def hashCode(): Int = name.hashCode() override def hashCode(): Int = name.hashCode()
/** @inheritDocs */ /** @inheritDocs
*/
override def toString(): String = name override def toString(): String = name
object SignatureValidity: object SignatureValidity:
/** /** Map a Boolean value to validity.
* Map a Boolean value to validity. *
* * - True: Valid
* - True: Valid * - False: Invalid
* - False: Invalid *
* * @param isValid
* @param isValid Whether the signature is valid or not. * Whether the signature is valid or not.
* @return The [[SignatureValidity]] selected for the given validity. * @return
*/ * The [[SignatureValidity]] selected for the given validity.
*/
def apply(isValid: Boolean): SignatureValidity = def apply(isValid: Boolean): SignatureValidity =
if isValid then Valid else Invalid if isValid then Valid else Invalid
given CanEqual[SignatureValidity, SignatureValidity] = CanEqual.derived given CanEqual[SignatureValidity, SignatureValidity] = CanEqual.derived
/** /** The [[Signature]] is valid.
* The [[Signature]] is valid. */
*/
case object Valid extends SignatureValidity("valid") case object Valid extends SignatureValidity("valid")
/** /** The [[Signature]] is invalid.
* The [[Signature]] is invalid. */
*/
case object Invalid extends SignatureValidity("invalid") case object Invalid extends SignatureValidity("invalid")
end SignatureValidity end SignatureValidity

View file

@ -1,7 +1,6 @@
package gs.crypto.v0 package gs.crypto.v0
/** /** Used to verify cryptographic signatures.
* Used to verify cryptographic signatures.
*/ */
trait SignatureVerifier[F[_]]: trait SignatureVerifier[F[_]]:
def verify(signature: Signature): F[SignatureValidity] def verify(signature: Signature): F[SignatureValidity]

View file

@ -3,23 +3,28 @@ package gs.crypto.v0
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
/** /** Used to calculate cryptographic signatures.
* Used to calculate cryptographic signatures.
*/ */
trait Signer[F[_]]: trait Signer[F[_]]:
/** /** Calculate a signature for the given data.
* Calculate a signature for the given data. *
* * @param data
* @param data The input data. * The input data.
* @return The calculated signature. * @return
*/ * The calculated signature.
*/
def sign(data: Array[Byte]): F[Signature] def sign(data: Array[Byte]): F[Signature]
/** /** Calculate a signature for the given data.
* Calculate a signature for the given data. *
* * @param data
* @param data The input data. * The input data.
* @param charset The character set of the input data. * @param charset
* @return The calculated signature. * The character set of the input data.
*/ * @return
def sign(data: String, charset: Charset = StandardCharsets.UTF_8): F[Signature] * The calculated signature.
*/
def sign(
data: String,
charset: Charset = StandardCharsets.UTF_8
): F[Signature]

View file

@ -2,16 +2,17 @@ package gs.crypto.v0
import cats.Applicative import cats.Applicative
/** /** Implementation of [[KeyLoader]] that loads keys directly from input strings.
* Implementation of [[KeyLoader]] that loads keys directly from input strings.
* *
* @param config The key loader configuration. * @param config
* The key loader configuration.
*/ */
class StringKeyLoader[F[_]: Applicative]( class StringKeyLoader[F[_]: Applicative](
val config: BaseKeyLoader.Config val config: BaseKeyLoader.Config
) extends BaseKeyLoader[F]: ) extends BaseKeyLoader[F]:
/** @inheritDocs */ /** @inheritDocs
*/
override def loadKey( override def loadKey(
location: String location: String
): F[Either[KeyLoadError, Array[Byte]]] = ): F[Either[KeyLoadError, Array[Byte]]] =

View file

@ -1,26 +1,27 @@
package gs.crypto.v0.eddsa package gs.crypto.v0.eddsa
import cats.effect.Sync import cats.effect.Sync
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
import gs.crypto.v0.Signature import gs.crypto.v0.Signature
import gs.crypto.v0.Signer import gs.crypto.v0.Signer
import java.nio.charset.Charset import java.nio.charset.Charset
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
/** /** Implementation of [[Signer]] that uses the Ed25519 system to calculate
* Implementation of [[Signer]] that uses the Ed25519 system to calculate
* signatures. * signatures.
* *
* See: [[Ed25519Verifier]] * See: [[Ed25519Verifier]]
* *
* @param privateKey The private key. * @param privateKey
* The private key.
*/ */
final class Ed25519Signer[F[_]: Sync]( final class Ed25519Signer[F[_]: Sync](
privateKey: Array[Byte] privateKey: Array[Byte]
) extends Signer[F]: ) extends Signer[F]:
private lazy val params = new Ed25519PrivateKeyParameters(privateKey) private lazy val params = new Ed25519PrivateKeyParameters(privateKey)
/** @inheritDocs */ /** @inheritDocs
*/
override def sign(data: Array[Byte]): F[Signature] = override def sign(data: Array[Byte]): F[Signature] =
Sync[F].delay { Sync[F].delay {
val s = new org.bouncycastle.crypto.signers.Ed25519Signer() val s = new org.bouncycastle.crypto.signers.Ed25519Signer()
@ -29,7 +30,8 @@ final class Ed25519Signer[F[_]: Sync](
Signature(s.generateSignature()) Signature(s.generateSignature())
} }
/** @inheritDocs */ /** @inheritDocs
*/
override def sign( override def sign(
data: String, data: String,
charset: Charset = StandardCharsets.UTF_8 charset: Charset = StandardCharsets.UTF_8

View file

@ -1,25 +1,26 @@
package gs.crypto.v0.eddsa package gs.crypto.v0.eddsa
import cats.effect.Sync import cats.effect.Sync
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters
import gs.crypto.v0.Signature import gs.crypto.v0.Signature
import gs.crypto.v0.SignatureVerifier
import gs.crypto.v0.SignatureValidity import gs.crypto.v0.SignatureValidity
import gs.crypto.v0.SignatureVerifier
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters
/** /** Implementation of [[SignatureVerifier]] that uses the Ed25519 system to
* Implementation of [[SignatureVerifier]] that uses the Ed25519 system to
* verify signatures. * verify signatures.
* *
* See: [[Ed25519Signer]] * See: [[Ed25519Signer]]
* *
* @param publicKey The public key. * @param publicKey
* The public key.
*/ */
final class Ed25519Verifier[F[_]: Sync]( final class Ed25519Verifier[F[_]: Sync](
publicKey: Array[Byte] publicKey: Array[Byte]
) extends SignatureVerifier[F]: ) extends SignatureVerifier[F]:
private lazy val params = new Ed25519PublicKeyParameters(publicKey) private lazy val params = new Ed25519PublicKeyParameters(publicKey)
/** @inheritDocs */ /** @inheritDocs
*/
override def verify(signature: Signature): F[SignatureValidity] = override def verify(signature: Signature): F[SignatureValidity] =
Sync[F].delay { Sync[F].delay {
val s = new org.bouncycastle.crypto.signers.Ed25519Signer() val s = new org.bouncycastle.crypto.signers.Ed25519Signer()