Hash fn

use "path:/usr/local/opt/libressl/lib" if osx and x86
use "path:/opt/homebrew/opt/libressl/lib" if osx and arm
use "lib:crypto"

use @MD4[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @MD5[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @RIPEMD160[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @SHA1[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @SHA224[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @SHA256[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @SHA384[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)
use @SHA512[Pointer[U8]](d: Pointer[U8] tag, n: USize, md: Pointer[U8] tag)

use "format"

interface HashFn
  """
  Produces a fixed-length byte array based on the input sequence.
  """
  fun apply(input: ByteSeq): Array[U8] val
    """
    The digest of `input`. The same input always gives the same digest, and
    every digest an implementation gives back has the same length.
    """

primitive MD4 is HashFn
  """
  Compute the MD4 message digest conforming to RFC 1320. Returns 16 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 16
      let arr = Array[U8].init(0, size)
      @MD4(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive MD5 is HashFn
  """
  Compute the MD5 message digest conforming to RFC 1321. Returns 16 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 16
      let arr = Array[U8].init(0, size)
      @MD5(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive RIPEMD160 is HashFn
  """
  Compute the RIPEMD160 message digest conforming to ISO/IEC 10118-3. Returns
  20 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 20
      let arr = Array[U8].init(0, size)
      @RIPEMD160(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive SHA1 is HashFn
  """
  Compute the SHA1 message digest conforming to US Federal Information
  Processing Standard FIPS PUB 180-4. Returns 20 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 20
      let arr = Array[U8].init(0, size)
      @SHA1(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive SHA224 is HashFn
  """
  Compute the SHA224 message digest conforming to US Federal Information
  Processing Standard FIPS PUB 180-4. Returns 28 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 28
      let arr = Array[U8].init(0, size)
      @SHA224(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive SHA256 is HashFn
  """
  Compute the SHA256 message digest conforming to US Federal Information
  Processing Standard FIPS PUB 180-4. Returns 32 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 32
      let arr = Array[U8].init(0, size)
      @SHA256(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive SHA384 is HashFn
  """
  Compute the SHA384 message digest conforming to US Federal Information
  Processing Standard FIPS PUB 180-4. Returns 48 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 48
      let arr = Array[U8].init(0, size)
      @SHA384(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive SHA512 is HashFn
  """
  Compute the SHA512 message digest conforming to US Federal Information
  Processing Standard FIPS PUB 180-4. Returns 64 bytes.
  """
  fun apply(input: ByteSeq): Array[U8] val =>
    recover
      let size: USize = 64
      let arr = Array[U8].init(0, size)
      @SHA512(input.cpointer(), input.size(), arr.cpointer())
      arr
    end

primitive ToHexString
  """
  Return the lower-case hexadecimal string representation of the given Array
  of U8.

  ```pony
  let hex = ToHexString(SHA256("Hello World"))
  ```
  """
  fun apply(bs: Array[U8] val): String =>
    let out = recover String(bs.size() * 2) end
    for c in bs.values() do
      out.append(Format.int[U8](c where
        fmt = FormatHexSmallBare, width = 2, fill = '0'))
    end
    consume out