In order to secure the documents in the blockring we use the SHAKE-224 sponge function. and we wrote a few perl routines to computes the different hash values.

The perl module KMAC.pm implements the NIST SP800-185* bytepad, left_encode, right_encode function. for the message authentication code KMAC (MAC-SHA3) using cSHAKE hash function (Secure Hash Algorithm KECCAK)

note:

  • for L < 160 I use KMAC128 and KMAC256 otherwise
  • cSHAKE is slightly different than NIST because of the 2 bits: 00 in message for the sponge function.

     # KMAC128(K, X, L, S): Validity Conditions: len(K) < 2^2040 and 0 ≤ L < 2^2040 and len(S) < 2^2040
     # newX = bytepad(encode_string(K), 168) || X || right_encode(L).
     # return cSHAKE128(newX, L, “KMAC”, S).
         
     # KMAC256(K, X, L, S): Validity Conditions: len(K) < 2^2040 and 0 ≤ L < 2^2040 and len(S) < 2^2040
     # newX = bytepad(encode_string(K), 136) || X || right_encode(L).
     # return cSHAKE256(newX, L, “KMAC”, S).
         
         
     # example for using S (Customization String)
     # *  cSHAKE128(public_key, 256, "", "key fingerprint")
     # *  cSHAKE128(email_contents, 256, "", "email signature")
    

Perl sub for KMAC:

sub KMAC {
  my ($K,$X,$L,$S) = @_;
  my $rate = ($L >= 160) ? 136 : 168;
  my $newX = &bytepad($K,$rate) . $X . &right_encode($L);
  printf qq(newX:%s\n),unpack'H*',$newX if $dbug;
  return &cSHAKE($newX,$L,"KMAC",$S);
}


sub cSHAKE {
  my ($X,$L,$N,$S) = @_;
  if ($N eq '' && $S eq '') {
    return &shake($L,$X); # L before X
  } else {
    # KECCAK[256/512](bytepad(encode_string(N) || encode_string(S), 168/136) || X ||  00, L).
    my $rate = ($L >= 160) ? 136 : 168;
    my $M = &bytepad($N.$S,$rate) . $X . "\x00"; # /!\ bug alert here != NIST document : "00" as 2 bits !!
    printf qq(M:%s\n),enc($M) if $dbug;
    my $kh = &shake($L,$M); # L before M
    return $kh;
  }
}

where shake is

sub shake { # use shake 128 for L < 160
  # see also [*][sponge]
  use Crypt::Digest::SHAKE;
  my $len = shift;
  my $x = ($len >= 160) ? 256 : 128; # selection of the sponge !
  my $msg = Crypt::Digest::SHAKE->new($x);
  $msg->add(join'',@_);
  my $digest = $msg->done(($len+7)/8);
  return $digest;
}

again here we use the 160 threshold for choosing between KECCAK[256] and KECCAK[512]

other miscellaneous functions

sub bytepad {
  my ($X,$w) = @_;
  my $z = &left_encode($w) . $X;
  while (length($z)/8 % $w != 0) {
    $z = $z . "\x00"
  }
  return $z;
}
sub left_encode { # for now limited to 32bit... (int32)
   my $i = shift;
   my $x = &encode_base256($i);
   my $n = length($x);
   my $s = pack('C',$n) . $x;
   return $s;

}
sub right_encode { # for now limited to 32bit... (int32)
   my $i = shift;
   my $x = &encode_base256($i);
   my $n = length($x);
   my $s = $x.pack('C',$n);
   return $s;
}
sub encode_base256 { # limited to integer for now, will need to extend to bigint later
 use integer;
  my ($n) = @_;
  my $e = '';
  return("\x00") if $n == 0;
  while ( $n ) {
    my $c = $n % 256;
    $e .=  pack'C',$c;
    $n = int $n / 256;
  }
  return scalar reverse $e;
}