Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing check in signature verification #7

Closed
moCello opened this issue Feb 9, 2024 · 0 comments · Fixed by #9
Closed

Missing check in signature verification #7

moCello opened this issue Feb 9, 2024 · 0 comments · Fixed by #9
Assignees
Labels
fix:bug Something isn't working
Milestone

Comments

@moCello
Copy link
Member

moCello commented Feb 9, 2024

Summary

From audit finding BLS-03:

Secure verification of BLS signatures requires several checks, as
described in the reference IETF Internet Draft
(section 2.7):

result = CoreVerify(PK, message, signature)

Inputs:
- PK, a public key in the format output by SkToPk.
- message, an octet string.
- signature, an octet string in the format output by CoreSign.

Outputs:
- result, either VALID or INVALID.

Procedure:
1. R = signature_to_point(signature)
2. If R is INVALID, return INVALID
3. If signature_subgroup_check(R) is INVALID, return INVALID
4. If KeyValidate(PK) is INVALID, return INVALID
5. xP = pubkey_to_point(PK)
6. Q = hash_to_point(message)
7. C1 = pairing(Q, xP)
8. C2 = pairing(R, P)
9. If C1 == C2, return VALID, else return INVALID

where KeyValidate() checks that a public key is a curve point in the
subgroup and is not the identity element.

However, the implementation in keys/public.rs does not do all these
required checks:

impl PublicKey {
    /// Verify a [`Signature`] by comparing the results of the two pairing
    /// operations: e(sig, g_2) == e(Hₒ(m), pk).
    pub fn verify(&self, sig: &Signature, msg: &[u8]) -> Result<(), Error> {
        let h0m = h0(msg);
        let p1 = dusk_bls12_381::pairing(&sig.0, &G2Affine::generator());
        let p2 = dusk_bls12_381::pairing(&h0m, &self.0);

        if p1.eq(&p2) {
            Ok(())
        } else {
            Err(Error::InvalidSignature)
        }
    }

Specifically, the public key may be checked to be on the curve and in
the subgroup when instantiated with from_bytes() or from a secret key,
however these do not guarantee that the point is not the identity. To
check the public key, the is_valid() defined in keys/public.rs may be
used.

The signature, as a point, would be checked to be valid (on the curve,
in the subgroup) when instantiated via the from_bytes() method of the
underlying library.

To verify that an invalid public key is accepted in signature generation
and verification, we can run the following test:

fn pktest() {

    let rng = &mut StdRng::seed_from_u64(0xbeef);
    let msg = random_message(rng);
    let sk = SecretKey::from(BlsScalar::zero());
    let pk = PublicKey::from(&sk);
    let sig = sk.sign_vulnerable(&msg);

    assert!(pk.verify(&sig, &msg).is_ok());
    assert!(pk.is_valid())
}

The first assert will pass, only the second will fail:

thread 'pktest' panicked at tests/signature.rs:24:5:
assertion failed: pk.is_valid()
moCello added a commit that referenced this issue Feb 23, 2024
moCello added a commit that referenced this issue Feb 23, 2024
moCello added a commit that referenced this issue Feb 23, 2024
@HDauven HDauven added the fix:bug Something isn't working label Apr 10, 2024
@HDauven HDauven added this to the Mainnet milestone Apr 10, 2024
@ureeves ureeves mentioned this issue Apr 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fix:bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants