Signatures
The implemented lattice-based signature schemes can be found in the module qfall_crypto::constructions::signature
. All of them implement the trait SignatureScheme
and thus the functions gen
, sign
, and vfy
for the key generation, signing and verification according to some public parameters, which are stored in the struct
of each implemented scheme.
With the provided functionality, it is easy to setup a scheme, and sign and verify messages:
use qfall_crypto::construction::signature::{SignatureScheme, FDH};
fn signing_and_verifying() {
// setup public parameters and generate key-pair
let mut fdh = FDH::init_gpv(10, 512, 42);
let (pk, sk) = fdh.gen();
// sign and verify a message
let sigma = fdh.sign("Hello World!".to_owned(), &sk, &pk);
assert!(fdh.vfy("Hello World!".to_owned(), &sigma, &pk))
}
The signature schemes we have implemented are FDH and PFDH signature schemes that build upon any kind of PSF. We have implemented a general implementation of FDH and PFDH, such that instantiation with any valid PSF and correspondingly chosen parameters directly yields a valid signature scheme.
As the FDH signature scheme is stateful and requires storage, the signature scheme must also be serializable. As the combination of
- an implementation that is as general as possible
- an implementation that is serializable
is quite hard to do in code, you might see some types called PhantomData
.
This is only used for type-binding of the correspondingly used PSF and does not take any memory or appears in the serialization of the signature scheme.
A serialization looks as follows:
use qfall_crypto::construction::{
hash::sha256::HashMatZq,
signature::{SignatureScheme, FDH},
};
use qfall_crypto::primitive::psf::PSFGPV;
use qfall_math::{integer::MatZ, integer_mod_q::MatZq, rational::MatQ};
fn serialize_and_deserialize() {
// setup public parameters and generate key-pair
let mut fdh = FDH::init_gpv(10, 1024, 42);
let (pk, sk) = fdh.gen();
// sign one message
let _ = fdh.sign("Hello World!".to_owned(), &sk, &pk);
// serialize the signature scheme
let fdh_string = serde_json::to_string(&fdh).unwrap();
// deserialize the signature scheme together with the storage
let fdh_deserialized: FDH<MatZq, (MatZ, MatQ), MatZ, MatZq, PSFGPV, HashMatZq> =
serde_json::from_str(&fdh_string).unwrap();
}
The part FDH<MatZq, (MatZ, MatQ), MatZ, MatZq, PSFGPV, HashMatZq>
might look a little irritating at first.
Our implementation for a signature scheme is as general as possible, hence each implementation of an FDH signature scheme uses the same FDH
struct, only with different parameters.
The parameters <MatZq, (MatZ, MatQ), MatZ, MatZq, PSFGPV, HashMatZq>
define the corresponding PSF and hash function used for the signature scheme.
Looking at the documentation in the crypto crate this might become clearer
FDH<A, Trapdoor, Domain, Range, T: PSF<A, Trapdoor, Domain, Range>, Hash: HashInto<Range>>
.
Each parameter determines the kind of signature scheme that is implemented.
For the ring-based FDH signature scheme, we have FDH<MatPolynomialRingZq, (MatPolyOverZ, MatPolyOverZ), MatPolyOverZ, MatPolynomialRingZq, PSFGPVRing, HashMatPolynomialRingZq>
which follows the same principle.
This idea was also applied to the PFDH signature scheme where the classical implementation follows the same pattern PFDH<MatZq, (MatZ, MatQ), MatZ, MatZq, PSFGPV, HashMatZq>
.