The vulnerability, a double public key signing function oracle attack, stems from the ability to sign a message using a secret key while providing an arbitrary, non-matching public key. This allows an attacker to create two signatures for the same message with the same R value but different S values, from which the private key can be calculated.
The root cause lies in the ed25519_dalek::secret::ExpandedSecretKey::sign and sign_prehashed functions, which were part of the public API and accepted a PublicKey as a parameter, without verifying its correspondence to the ExpandedSecretKey.
This vulnerability could be triggered in two ways:
- Directly calling the public
ExpandedSecretKey::sign or sign_prehashed methods.
- Indirectly by first creating a
Keypair with a mismatched public key and then calling the Keypair::sign method. The creation of a malicious Keypair was possible because its fields were public, and the Keypair::from_bytes constructor did not validate the key correspondence.
The patch addresses these issues comprehensively:
- It makes the
ExpandedSecretKey struct and its signing methods pub(crate), removing them from the public API and preventing direct misuse.
- It makes the fields of the
Keypair struct pub(crate), preventing manual instantiation with mismatched keys.
- It adds a validation step in
Keypair::from_bytes to ensure the public key is correctly derived from the secret key.
By enforcing the invariant that a Keypair's public key must be derived from its secret key, the Keypair::sign function is no longer an attack vector.