All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.plasmalabs.crypto.hash.digest.digest.scala Maven / Gradle / Ivy

The newest version!
package org.plasmalabs.crypto.hash

import cats.data.{Validated, ValidatedNec}

import scala.language.implicitConversions

package object digest {

  /**
   * Represents a digest with a size and the ability to convert to and from bytes.
   *
   * @tparam T the implemented digest type
   */
  trait Digest[T] {

    /**
     * The digest size.
     *
     * @return the size of the digest
     */
    def size: Int

    /**
     * Gets a validated digest from an array of bytes.
     *
     * @param bytes the bytes to be converted into a digest
     * @return a validated digest with a possible invalid digest error
     */
    def from(bytes: Array[Byte]): ValidatedNec[InvalidDigestFailure, T]

    /**
     * Gets the bytes representing the digest.
     *
     * @param d the digest to convert to bytes
     * @return the bytes of the digest
     */
    def bytes(d: T): Array[Byte]

    def empty: T = from(Array.fill(size)(0: Byte))
      .getOrElse(throw new Error(s"Failed to validate empty digest of size $size!"))
  }

  object Digest {
    def apply[T](implicit instance: Digest[T]): Digest[T] = instance

    trait Ops[T] {
      def typeClassInstance: Digest[T]
      def self: T

      def size: Int = typeClassInstance.size
      def bytes: Array[Byte] = typeClassInstance.bytes(self)
    }

    trait ToDigestOps {

      implicit def toDigestOps[T](target: T)(implicit tc: Digest[T]): Ops[T] = new Ops[T] {
        def typeClassInstance: Digest[T] = tc
        def self: T = target
      }
    }
  }

  case class Digest32(val value: Array[Byte])

  object Digest32 {
    val size = 32

    /**
     * Gets a validated Digest32 guaranteed to be 32 bytes long.
     *
     * @param bytes the bytes to convert to a digest
     * @return the digest or an invalid error
     */
    def validated(bytes: Array[Byte]): ValidatedNec[InvalidDigestFailure, Digest32] =
      Validated.condNec(bytes.length == size, Digest32(bytes), IncorrectSize)
  }

  case class Digest64(val value: Array[Byte])

  object Digest64 {
    val size = 64

    /**
     * Gets a validated Digest64 guaranteed to be 64 bytes long.
     *
     * @param bytes the bytes to convert to a digest
     * @return the digest or an invalid error
     */
    def validated(bytes: Array[Byte]): ValidatedNec[InvalidDigestFailure, Digest64] =
      Validated.condNec(bytes.length == size, Digest64(bytes), IncorrectSize)
  }

  sealed trait InvalidDigestFailure
  case object IncorrectSize extends InvalidDigestFailure

  trait Instances {

    implicit val digestDigest32: Digest[Digest32] = new Digest[Digest32] {
      override def size: Int = Digest32.size

      override def from(bytes: Array[Byte]): ValidatedNec[InvalidDigestFailure, Digest32] = Digest32.validated(bytes)

      override def bytes(d: Digest32): Array[Byte] = d.value
    }

    implicit val digestDigest64: Digest[Digest64] = new Digest[Digest64] {
      override def size: Int = Digest64.size

      override def from(bytes: Array[Byte]): ValidatedNec[InvalidDigestFailure, Digest64] = Digest64.validated(bytes)

      override def bytes(d: Digest64): Array[Byte] = d.value
    }
  }

  trait DigestImplicits extends Instances with Digest.ToDigestOps

  object implicits extends DigestImplicits
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy