From 0e142c088841bdb44861e6d95f8e813a78f73ba5 Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Fri, 15 Mar 2024 11:34:43 +0100 Subject: [PATCH] Added verification to certificate (#82) * Added verification method to certificate * Update da/common.py typo short -> sort Co-authored-by: gusto * Fix test imports * Added verification comment --------- Co-authored-by: gusto --- da/common.py | 26 ++++++++++++++++++++++---- da/dispersal.py | 12 ++++-------- da/test_dispersal.py | 18 ++++++------------ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/da/common.py b/da/common.py index 72388b2..d343bbd 100644 --- a/da/common.py +++ b/da/common.py @@ -1,10 +1,10 @@ from dataclasses import dataclass -from itertools import chain, zip_longest -from typing import List, Generator, Self - - +from hashlib import sha3_256 +from itertools import chain, zip_longest, compress +from typing import List, Generator, Self, Sequence from eth2spec.eip7594.mainnet import Bytes32, KZGCommitment as Commitment +from py_ecc.bls import G2ProofOfPossession as bls_pop class NodeId(Bytes32): @@ -55,3 +55,21 @@ class Certificate: aggregated_column_commitment: Commitment row_commitments: List[Commitment] + def verify(self, nodes_public_keys: List[BLSPublickey]) -> bool: + """ + List of nodes public keys should be a trusted list of verified proof of possession keys. + Otherwise, we could fall under the Rogue Key Attack + `assert all(bls_pop.PopVerify(pk, proof) for pk, proof in zip(node_public_keys, pops))` + """ + # we sort them as the signers bitfield is sorted by the public keys as well + signers_keys = list(compress(sorted(nodes_public_keys), self.signers)) + message = build_attestation_message(self.aggregated_column_commitment, self.row_commitments) + return bls_pop.AggregateVerify(signers_keys, [message]*len(signers_keys), self.aggregated_signatures) + + +def build_attestation_message(aggregated_column_commitment: Commitment, row_commitments: Sequence[Commitment]) -> bytes: + hasher = sha3_256() + hasher.update(bytes(aggregated_column_commitment)) + for c in row_commitments: + hasher.update(bytes(c)) + return hasher.digest() \ No newline at end of file diff --git a/da/dispersal.py b/da/dispersal.py index aade4f9..9a6fb12 100644 --- a/da/dispersal.py +++ b/da/dispersal.py @@ -4,7 +4,7 @@ from typing import List, Optional, Generator, Sequence from py_ecc.bls import G2ProofOfPossession as bls_pop -from da.common import Certificate, NodeId, BLSPublickey, Bitfield +from da.common import Certificate, NodeId, BLSPublickey, Bitfield, build_attestation_message from da.encoder import EncodedData from da.verifier import DABlob, Attestation @@ -19,9 +19,9 @@ class DispersalSettings: class Dispersal: def __init__(self, settings: DispersalSettings): self.settings = settings - # sort nodes_ids and related public keys + # sort over public keys self.settings.nodes_ids, self.settings.nodes_pubkey = zip( - *sorted(zip(self.settings.nodes_ids, self.settings.nodes_pubkey), key=lambda x: x[0]) + *sorted(zip(self.settings.nodes_ids, self.settings.nodes_pubkey), key=lambda x: x[1]) ) def _prepare_data(self, encoded_data: EncodedData) -> Generator[DABlob, None, None]: @@ -71,11 +71,7 @@ class Dispersal: @staticmethod def _build_attestation_message(encoded_data: EncodedData) -> bytes: - hasher = sha3_256() - hasher.update(bytes(encoded_data.aggregated_column_commitment)) - for c in encoded_data.row_commitments: - hasher.update(bytes(c)) - return hasher.digest() + return build_attestation_message(encoded_data.aggregated_column_commitment, encoded_data.row_commitments) def disperse(self, encoded_data: EncodedData) -> Optional[Certificate]: attestations = [] diff --git a/da/test_dispersal.py b/da/test_dispersal.py index dabe830..500cd57 100644 --- a/da/test_dispersal.py +++ b/da/test_dispersal.py @@ -1,14 +1,13 @@ from hashlib import sha3_256 from unittest import TestCase -from .encoder import DAEncoderParams, DAEncoder -from .test_encoder import TestEncoder - +from da.encoder import DAEncoderParams, DAEncoder +from da.test_encoder import TestEncoder +from da.verifier import DAVerifier, DABlob from da.common import NodeId, Attestation, Bitfield from da.dispersal import Dispersal, EncodedData, DispersalSettings -from py_ecc.bls import G2ProofOfPossession as bls_pop -from .verifier import DAVerifier, DABlob +from py_ecc.bls import G2ProofOfPossession as bls_pop class TestDispersal(TestCase): @@ -46,7 +45,7 @@ class TestDispersal(TestCase): self.assertEqual(certificate.row_commitments, []) self.assertIsNotNone(certificate.aggregated_signatures) self.assertTrue( - bls_pop.AggregateVerify(self.public_keys, [mock_message]*len(self.public_keys), certificate.aggregated_signatures) + certificate.verify(self.public_keys) ) def test_disperse(self): @@ -64,12 +63,7 @@ class TestDispersal(TestCase): certificate = self.dispersal.disperse(encoded_data) self.assertIsNotNone(certificate) - self.assertTrue( - bls_pop.AggregateVerify( - self.public_keys[:self.dispersal.settings.threshold], - [self.dispersal._build_attestation_message(encoded_data)]*self.dispersal.settings.threshold, - certificate.aggregated_signatures - ) + self.assertTrue(certificate.verify(self.public_keys) ) self.assertEqual( certificate.signers,