Internet-Draft Fiat-Shamir Transformation May 2025
Orrù Expires 18 November 2025 [Page]
Workgroup:
Zkproof
Internet-Draft:
draft-orru-zkproof-fiat-shamir-latest
Published:
Intended Status:
Informational
Expires:
Author:
M. Orrù
CNRS

Fiat-Shamir Transformation

Abstract

This document describes the Fiat-Shamir transformation via a duplex sponge interface that is capable of supporting a number of different hash functions, to "absorb" elements from different domains, and produce pseudoranom elements "squeezing" from the hash object.

In addition, the specification provides codes, a way to absorb specific data types.

About This Document

This note is to be removed before publishing as an RFC.

The latest revision of this draft can be found at https://mmaker.github.io/draft-zkproof-sigma-protocols/draft-orru-zkproof-fiat-shamir.html. Status information for this document may be found at https://datatracker.ietf.org/doc/draft-orru-zkproof-fiat-shamir/.

Discussion of this document takes place on the Crypto Forum mailing list (mailto:cfrg@ietf.org), which is archived at https://mailarchive.ietf.org/arch/browse/cfrg. Subscribe at https://www.ietf.org/mailman/listinfo/cfrg/.

Source for this draft and an issue tracker can be found at https://github.com/mmaker/draft-zkproof-sigma-protocols.

Status of This Memo

This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.

Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.

Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."

This Internet-Draft will expire on 18 November 2025.

Table of Contents

1. Introduction

The Fiat-Shamir transformation relies on a hash function that can absorb inputs incrementally and squeeze variable-length unpredictable messages. On a high level, it consists of three main components:

The core actions supported are:

The API follows the template of duplex sponges.

2. The API

A duplex sponge has the following interface:

class DuplexSpongeInterface: type Unit

  def new(iv: bytes) -> hash_state
  def absorb(self, x: list[Unit])
  def squeeze(self, length: int) -> list[Unit]
  def finalize(self)

where

The above can be extended to support absorption and squeeze from different domains than the domain in which the hash function is initialized over. Such extensions are called codecs.

3. Overwrite mode implementation from permutation functions

A duplex sponge in overwrite mode is based on a permutation function P that maps a vector of r + c elements of type Unit elements. It implements the DuplexSpongeInterface.

3.1. Initialization

This is the constructor for a duplex sponge object. It is initialized with a 32-byte value that initializes the sponge state.

new(iv)

assert len(iv) == 32
self.absorb_index = 0
self.squeeze_index = 0
self.rate = self.state.R
self.capacity = self.state.N - self.state.R

3.2. Absorb

The absorb function incorporates data into the duplex sponge state.

absorb(self, input)

Inputs:

-  self, the current duplex sponge object
-  input, the input to be absorbed

Constants:
    permutation

    self.squeeze_index = self.rate

Procedure:

1. while len(input) != 0:
2.   if self.absorb_index == self.rate:
3.     self.permutation_state.permute()
4.     self.absorb_index = 0
5.   chunk_size = min(self.rate - self.absorb_index, len(input))
6.   next_chunk = input[:chunk_size]
7.   self.permutation_state[self.absorb_index : self.absorb_index + chunk_size] = next_chunk
8.   self.absorb_index += chunk_size
9.   input = input[chunk_size:]

3.3. Squeeze

The squeeze operation extracts output elements from the sponge state, which are uniformly distributed and can be used as a digest, a key stream, or other cryptographic material.

squeeze(self, length)

Inputs:

- self, the current duplex sponge object
- length, the number of bytes to be squeezed out of the sponge

Outputs:

- digest, a byte array of `length` elements uniformly distributed

Procedure:

1. output = b''
2. while length != 0:
3.     if self.squeeze_index == self.rate:
4.         self.permutation_state.permute()
5.         self.squeeze_index = 0
6.     chunk_size = min(self.rate - self.squeeze_index, length)
7.     self.squeeze_index += chunk_size
8.     length -= chunk_size
9.     output += bytes(self.permutation_state[self.squeeze_index:self.squeeze_index+chunk_size])
10. return output

3.5. Keccak

Keccak-f is the permutation function underlying~[SHA3]. We instantiate DuplexSponge with Keccak-f[1600], using rate R = 136 and capacity C = 64.

4. SHAKE128 compatibility [WIP]

SHAKE128 is a variable-length hash function based on the Keccak sponge construction [SHA3]. It belongs to the SHA-3 family but offers a flexible output length, and provides 128 bits of security against collision attacks, regardless of the output length requested.

4.1. Initialization

new(self, label)

Inputs:

- label, a byte array

Outputs:

-  a hash state interface

1. h = shake_128(label)
2. return h

4.2. SHAKE128 Absorb

absorb(hash_state, x)

Inputs:

- hash_state, a hash state
- x, a byte array

1. h.update(x)

This method is also re-exported as absorb_bytes.

4.3. SHAKE128 Squeeze

squeeze(hash_state, length)

Inputs:

- hash_state, the hash state
- length, the number of elements to be squeezed

1. h.digest(length)

This method is also re-exported as squeeze_bytes.

5. Codecs registry

5.1. P-384 (secp384r1)

5.1.1. Absorb scalars

absorb_scalars(hash_state, scalars)

Inputs:

- hash_state, the hash state
- scalars, a list of elements of P-384's scalar field

Constants:

- scalar_byte_length = ceil(384/8)

1. for scalar in scalars:
2.     hash_state.absorb_bytes(scalar_to_bytes(scalar))

Where the function scalar_to_bytes is defined in {#notation}

5.1.2. Absorb elements

absorb_elements(hash_state, elements)

Inputs:

- hash_state, the hash state
- elements, a list of P-384 group elements

1. for element in elements:
2.     hash_state.absorb_bytes(ecpoint_to_bytes(element))

5.1.3. Squeeze scalars

squeeze_scalars(hash_state, length)

Inputs:

- hash_state, the hash state
- length, an unsiged integer of 64 bits determining the output length.

1. for i in range(length):
2.     scalar_bytes = hash_state.squeeze_bytes(field_bytes_length + 16)
3.     scalars.append(bytes_to_scalar_mod_order(scalar_bytes))

6. Notation and Terminology

For an elliptic curve, we consider two fields, the coordinate fields, which indicates the base field, the field over which the elliptic curve equation is defined, and the scalar field, over which the scalar operations are performed.

The following functions and notation are used throughout the document.

7. References

7.1. Normative References

[RFC8017]
Moriarty, K., Ed., Kaliski, B., Jonsson, J., and A. Rusch, "PKCS #1: RSA Cryptography Specifications Version 2.2", RFC 8017, DOI 10.17487/RFC8017, , <https://www.rfc-editor.org/rfc/rfc8017>.

7.2. Informative References

[SHA3]
"SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions", n.d., <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>.

Author's Address

Michele Orrù
CNRS