#!/usr/bin/env perl

##
## Author......: See docs/credits.txt
## License.....: MIT
##

use strict;
use warnings;

use Crypt::OpenSSH::ChachaPoly;

sub module_constraints { [[32, 32], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] }

sub module_generate_hash
{
  my $word   = shift;
  my $salt   = shift;
  my $param  = shift;
  my $param2 = shift;
  my $param3 = shift;

  my $counter;
  my $offset;
  my $iv;

  if (defined $param)
  {
    $counter = $param;
    $offset  = $param2;
    $iv      = $param3;
  }
  else
  {
    $counter = "0400000000000003";
    $offset  = int (rand (63));
    $iv      = "0200000000000001";
  }

  my $plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz0a2b4c6d8e";
  my $eight_byte_iv = pack ("H*", $iv);
  my $eight_byte_counter = pack ("H*", $counter);
  #my $pad_len = 32 - length ($word);
  #my $key = $word . "\0" x $pad_len;
  my $key = $word;

  my $cipher = Crypt::OpenSSH::ChachaPoly->new ($key);

  $cipher->ivsetup ($eight_byte_iv, $eight_byte_counter);

  my $enc = $cipher->encrypt ($plaintext);

  my $enc_offset = substr ($enc, $offset, 8);

  my $hash_buf = $enc_offset;

  my $hash = sprintf ("\$chacha20\$\*%s\*%d\*%s\*%s\*%s", $counter, $offset, $iv, unpack ("H*", substr ($plaintext, $offset, 8)), unpack ("H*", $enc_offset));

  return $hash;
}

sub module_verify_hash
{
  my $line = shift;

  my $index1 = index ($line, ':');

  return if ($index1 < 0);

  my $hash_in = substr ($line, 0, $index1);

  my $word = substr ($line, $index1 + 1);

  return if (length ($hash_in) < 11);

  return unless (substr ($hash_in, 0, 11) eq "\$chacha20\$\*");

  my @data = split ('\*', $hash_in);

  return unless (scalar (@data) == 6);

  my $param  = $data[1]; # counter
  my $param2 = $data[2]; # offset
  my $param3 = $data[3]; # iv

  return unless defined $param;
  return unless defined $param2;
  return unless defined $param3;
  return unless defined $word;

  $word = pack_if_HEX_notation ($word);

  my $new_hash = module_generate_hash ($word, undef, $param, $param2, $param3);

  return ($new_hash, $word);
}

1;