mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
feat(ml-dsa): Implement verifying_key method for SigningKey (#1008)
Implement the `signature::Keypair` trait for `SigningKey`, enabling derivation of a `VerifyingKey` from a bare signing key. Also adds inherent `verifying_key` method to act as a convenience shim around the trait implementation, ensuring a single source of logic and improving interoperability. Signed-off-by: Francesco Rollo <eferollo@gmail.com>
This commit is contained in:
@@ -504,6 +504,23 @@ impl<P: MlDsaParams> SigningKey<P> {
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// This auxiliary function derives a `VerifyingKey` from a bare
|
||||
/// `SigningKey` (even in the absence of the original seed).
|
||||
///
|
||||
/// This is a utility function that is useful when importing the private key
|
||||
/// from an external source which does not export the seed and does not
|
||||
/// provide the precomputed public key associated with the private key
|
||||
/// itself.
|
||||
///
|
||||
/// `SigningKey` implements `signature::Keypair`: this inherent method is
|
||||
/// retained for convenience, so it is available for callers even when the
|
||||
/// `signature::Keypair` trait is out-of-scope.
|
||||
pub fn verifying_key(&self) -> VerifyingKey<P> {
|
||||
let kp: &dyn signature::Keypair<VerifyingKey = VerifyingKey<P>> = self;
|
||||
|
||||
kp.verifying_key()
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Signer` implementation for `SigningKey` uses the optional deterministic variant of ML-DSA, and
|
||||
@@ -524,6 +541,26 @@ impl<P: MlDsaParams> MultipartSigner<Signature<P>> for SigningKey<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `KeyPair` implementation for `SigningKey` allows to derive a `VerifyingKey` from
|
||||
/// a bare `SigningKey` (even in the absence of the original seed).
|
||||
impl<P: MlDsaParams> signature::Keypair for SigningKey<P> {
|
||||
type VerifyingKey = VerifyingKey<P>;
|
||||
|
||||
/// This is a utility function that is useful when importing the private key
|
||||
/// from an external source which does not export the seed and does not
|
||||
/// provide the precomputed public key associated with the private key
|
||||
/// itself.
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
let As1 = &self.A_hat * &self.s1_hat;
|
||||
let t = &As1.ntt_inverse() + &self.s2;
|
||||
|
||||
/* Discard t0 */
|
||||
let (t1, _) = t.power2round();
|
||||
|
||||
VerifyingKey::new(self.rho.clone(), t1, Some(self.A_hat.clone()), None)
|
||||
}
|
||||
}
|
||||
|
||||
/// The `RandomizedSigner` implementation for `SigningKey` only supports signing with an empty
|
||||
/// context string. If you would like to include a context string, use the [`SigningKey::sign`]
|
||||
/// method.
|
||||
@@ -947,6 +984,25 @@ mod test {
|
||||
encode_decode_round_trip_test::<MlDsa87>();
|
||||
}
|
||||
|
||||
fn public_from_private_test<P>()
|
||||
where
|
||||
P: MlDsaParams + PartialEq,
|
||||
{
|
||||
let kp = P::key_gen_internal(&Array::default());
|
||||
let sk = kp.signing_key;
|
||||
let vk = kp.verifying_key;
|
||||
let vk_derived = sk.verifying_key();
|
||||
|
||||
assert!(vk == vk_derived);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_from_private() {
|
||||
public_from_private_test::<MlDsa44>();
|
||||
public_from_private_test::<MlDsa65>();
|
||||
public_from_private_test::<MlDsa87>();
|
||||
}
|
||||
|
||||
fn sign_verify_round_trip_test<P>()
|
||||
where
|
||||
P: MlDsaParams,
|
||||
|
||||
Reference in New Issue
Block a user