Pbkdf2 sha256

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 @PKCS5_PBKDF2_HMAC[I32](
  pass: Pointer[U8] tag, passlen: I32,
  salt: Pointer[U8] tag, saltlen: I32, iter: I32,
  digest: Pointer[_EVPMD],
  keylen: I32, out: Pointer[U8] tag) if "openssl_1.1.x" or "openssl_3.0.x" or "libressl"

primitive Pbkdf2Sha256
  """
  Derive a key from a password using PBKDF2 with HMAC-SHA-256 as the PRF,
  as defined in RFC 2898.

  Returns a key of the requested length, or raises an error if the derivation
  fails (e.g., zero iterations).

  Supported on OpenSSL 1.1.x, OpenSSL 3.0.x, and LibreSSL.

  ```pony
  let key = Pbkdf2Sha256("password", "salt", 4096, 32)?
  ```
  """
  fun tag apply(password: ByteSeq, salt: ByteSeq, iterations: U32,
    key_length: USize): Array[U8] val ?
  =>
    ifdef "openssl_1.1.x" or "openssl_3.0.x" or "libressl" then
      recover
        let arr = Array[U8].init(0, key_length)
        let rc = @PKCS5_PBKDF2_HMAC(
          password.cpointer(), password.size().i32(),
          salt.cpointer(), salt.size().i32(),
          iterations.i32(),
          @EVP_sha256(),
          key_length.i32(),
          arr.cpointer())
        if rc != 1 then error end
        arr
      end
    else
      compile_error "You must select an SSL version to use."
    end