Beispiel #1
0
    def test_bbs_from_public_key(self):
        bls_key_pair = BlsKeyPair.generate_g2()
        bbs_key_pair = BlsKeyPair(bls_key_pair.public_key)

        self.assertIsNone(bbs_key_pair.secret_key)

        public_key = bbs_key_pair.get_bbs_key(1)

        self.assertIsNotNone(public_key)
        self.assertIsNotNone(bbs_key_pair.public_key)
        self.assertIsNone(bbs_key_pair.secret_key)

        self.assertEqual(196, len(public_key.public_key))
        self.assertEqual(32, len(bls_key_pair.secret_key))
Beispiel #2
0
    def test_generate_g2_key_with_seed(self):
        seed = 'just a seed'
        key_pair = BlsKeyPair.generate_g2(seed)
        self.assertIsNotNone(key_pair, "Key pair should not be None")
        self.assertIsNotNone(key_pair.public_key,
                             "Key pair should have public key")
        self.assertIsNotNone(key_pair.secret_key,
                             "Key pair should have secret key")
        self.assertTrue(key_pair.is_g2, "Key should be G2 key")
        self.assertFalse(key_pair.is_g1, "Key should NOT be G1 key")

        self.assertEqual(BlsKeyPair.secret_key_size(),
                         len(key_pair.secret_key))
        self.assertEqual(BlsKeyPair.public_g2_key_size(),
                         len(key_pair.public_key))
def verify_signed_messages_bls12381g2(messages: List[bytes], signature: bytes,
                                      public_key: bytes) -> bool:
    """
    Verify an ed25519 signed message according to a public verification key.

    Args:
        signed: The signed messages
        public_key: The public key to use in verification

    Returns:
        True if verified, else False

    """
    key_pair = BlsKeyPair(public_key=public_key)
    messages = [message.decode("utf-8") for message in messages]

    verify_request = VerifyRequest(key_pair=key_pair,
                                   signature=signature,
                                   messages=messages)

    try:
        return bbs_verify(verify_request)
    except (
            FfiException,
            NativeBbsException,
    ) as error:  # would be nice to be able to distinct between false and error
        raise BbsException("Unable to verify BBS+ signature") from error
Beispiel #4
0
 def test_verify_invalid_number_of_messages(self):
     verify_key = BlsKeyPair(self.public_bls_key_alice)
     request = VerifyRequest(verify_key, self.valid_signature, self.messages[:-1])
     result = verify(request)
     self.assertFalse(result,
                      "Verification should fail because number of verification messages differ from the "
                      "messages that were signed.")
Beispiel #5
0
    def setUp(self) -> None:
        self.public_bls_key_alice = b"\x86M\xc0cUPQ\xdb\xdblE\x87E\x832p8\xf5\xb9\xbeM\x05\xf1G\x9emHe\x99\xf0T\xbfn\x85\x18\xdb\x86'W\x1c\xe3\x8aG\x97S\x01\xda\xfe\x0e\x15)\x144I\xf9\xd0:\xcc\xdb\xc5\xc26\x10\xf9@\xaa\x18\xf5,6Es\xfd\xc7\xf1tcZ\x98\xfe\xd6\xcct\xbfk\xfb\x9f\xf1\xad)\x15\x88w\x80\xdd\xea"

        self.secret_bls_key_alice = b'\x06\xe7w\xf4\x90\x0e\xacK\xb7\x94l\x00/\xaaFD\x1c\xff\x9c\xad\xdcq\xed\xb6#%\x7fu\xc7\x8c\xfe\x9c'

        self.public_bls_key_bob = b'\x91\xc1\xe7\xf6\xf3h&\x97\xc8%\xe5\x18xy*W\xbcy\x0e{\x9f\xba\xde\xfe\x14\x14\xb9(K_6=\xe0\x80\xe92\xe4%\x13\x88\x86\xaf\x86\x00\xeb!\x95B\x04[\xaa\xeaU\x82\xbe\x9fts\x96\xe9\xaeG\xf1!K\xe1\xb8M+\x03\x18\xd6\xa2\xa1\xac\x1f\xa5}\x12z\x17QjU\x99\x12Q\xfb2\x03\x86\xc7O4\x9d7'

        self.secret_bls_key_bob = b'j\xf2\xf1abM\xd3\xa5Kumz6\xeaK\x1c\x03\xac\x97"(' \
                                  b'kR\xdd\x84#]\xea\x82\xfb\x86\xf1 '
        self.messages = [
            "message 1",
            "message 2",
            "message 3",
            "message 4",
            "message 5",
        ]

        self.invalid_messages = [
            "message 1",
            "message X",
            "message 3",
            "message X",
            "message 5",
        ]

        self.valid_signature = sign(
            SignRequest(
                BlsKeyPair(
                    self.public_bls_key_alice,
                    self.secret_bls_key_alice
                ),
                self.messages
            )
        )

        self.invalid_signature = sign(
            SignRequest(
                BlsKeyPair(
                    self.public_bls_key_alice,
                    self.secret_bls_key_bob
                ),
                self.messages
            )
        )
    def test_blind_commitment_single_message(self):
        key = BlsKeyPair.generate_g2()
        public_key = key.get_bbs_key(1)

        commitment = create_blinded_commitment(
            CreateBlindedCommitmentRequest(
                public_key=public_key,
                messages=[IndexedMessage('message_0', 0)],
                nonce=b'1234'))

        self.assertIsNotNone(commitment)
        self.assertIsNotNone(commitment.blinding_factor)
        self.assertIsNotNone(commitment.blind_sign_context)
        self.assertIsNotNone(commitment.commitment)
    def test_sign_single_message(self):
        bls_key = BlsKeyPair.generate_g2()
        public_key = bls_key.get_bbs_key(2)

        messages: List[IndexedMessage] = [
            IndexedMessage('message_0', 0),
            IndexedMessage('message_1', 1)
        ]

        nonce = b'12345'

        commitment = create_blinded_commitment(
            CreateBlindedCommitmentRequest(public_key, messages, nonce))
        blind_signature = blind_sign(
            BlindSignRequest(bls_key, public_key, commitment.commitment,
                             messages))

        self.assertIsNotNone(blind_signature)
def create_bls12381g2_keypair(seed: bytes = None) -> Tuple[bytes, bytes]:
    """
    Create a public and private bls12381g2 keypair from a seed value.

    Args:
        seed: Seed for keypair

    Returns:
        A tuple of (public key, secret key)

    """
    if not seed:
        seed = random_seed()

    try:
        key_pair = BlsKeyPair.generate_g2(seed)
        return key_pair.public_key, key_pair.secret_key
    except (Exception) as error:
        raise BbsException("Unable to create keypair") from error
def sign_messages_bls12381g2(messages: List[bytes], secret: bytes):
    """Sign messages using a bls12381g2 private signing key.

    Args:
        messages (List[bytes]): The messages to sign
        secret (bytes): The private signing key

    Returns:
        bytes: The signature

    """

    messages = [message.decode("utf-8") for message in messages]
    try:
        key_pair = BlsKeyPair.from_secret_key(secret)
        sign_request = SignRequest(key_pair=key_pair, messages=messages)
    except (
            FfiException,
            NativeBbsException,
    ) as error:  # would be nice to be able to distinct between false and error
        raise BbsException("Unable to sign messages") from error

    return bbs_sign(sign_request)
Beispiel #10
0
 def test_get_secret_key_size(self):
     self.assertEqual(BlsKeyPair.secret_key_size(), 32,
                      "Secret key should be of length 32")
Beispiel #11
0
 def test_verify_invalid_public_key(self):
     verify_key = BlsKeyPair(self.public_bls_key_bob)
     request = VerifyRequest(verify_key, self.valid_signature, self.messages)
     result = verify(request)
     self.assertFalse(result, "Verification should fail due to invalid public key")
Beispiel #12
0
 def test_verify_success(self):
     verify_key = BlsKeyPair(self.public_bls_key_alice)
     request = VerifyRequest(verify_key, self.valid_signature, self.messages)
     result = verify(request)
     self.assertTrue(result, "Verification should be successful")
Beispiel #13
0
    async def derive_proof(
        self,
        *,
        proof: dict,
        document: dict,
        reveal_document: dict,
        document_loader: DocumentLoaderMethod,
        nonce: bytes = None,
    ):
        """Derive proof for document, return dict with derived document and proof."""

        # Validate that the input proof document has a proof compatible with this suite
        if proof.get("type") not in self.supported_derive_proof_types:
            raise LinkedDataProofException(
                f"Proof document proof incompatible, expected proof types of"
                f" {self.supported_derive_proof_types}, received " +
                proof["type"])

        # Extract the BBS signature from the input proof
        signature = b64_to_bytes(proof["proofValue"])

        # Initialize the BBS signature suite
        # This is used for creating the input document verification data
        # NOTE: both suite._create_verify_xxx_data and self._create_verify_xxx_data
        # are used in this file. They have small changes in behavior
        suite = BbsBlsSignature2020(key_pair=self.key_pair)

        # Initialize the derived proof
        derived_proof = self.proof.copy() if self.proof else {}

        # Ensure proof type is set
        derived_proof["type"] = self.signature_type

        # Get the input document and proof statements
        document_statements = suite._create_verify_document_data(
            document=document, document_loader=document_loader)
        proof_statements = suite._create_verify_proof_data(
            proof=proof, document=document, document_loader=document_loader)

        # Transform any blank node identifiers for the input
        # document statements into actual node identifiers
        # e.g _:c14n0 => urn:bnid:_:c14n0
        transformed_input_document_statements = (
            self._transform_blank_node_ids_into_placeholder_node_ids(
                document_statements))

        # Transform the resulting RDF statements back into JSON-LD
        compact_input_proof_document = jsonld.from_rdf(
            "\n".join(transformed_input_document_statements))

        # Frame the result to create the reveal document result
        reveal_document_result = jsonld.frame(
            compact_input_proof_document,
            reveal_document,
            {"documentLoader": document_loader},
        )

        # Canonicalize the resulting reveal document
        reveal_document_statements = suite._create_verify_document_data(
            document=reveal_document_result, document_loader=document_loader)

        # Get the indices of the revealed statements from the transformed input document
        # offset by the number of proof statements
        number_of_proof_statements = len(proof_statements)

        # Always reveal all the statements associated to the original proof
        # these are always the first statements in the normalized form
        proof_reveal_indices = [
            indice for indice in range(number_of_proof_statements)
        ]

        # Reveal the statements indicated from the reveal document
        document_reveal_indices = list(
            map(
                lambda reveal_statement: transformed_input_document_statements.
                index(reveal_statement) + number_of_proof_statements,
                reveal_document_statements,
            ))

        # Check there is not a mismatch
        if len(document_reveal_indices) != len(reveal_document_statements):
            raise LinkedDataProofException(
                "Some statements in the reveal document not found in original proof"
            )

        # Combine all indices to get the resulting list of revealed indices
        reveal_indices = [*proof_reveal_indices, *document_reveal_indices]

        # Create a nonce if one is not supplied
        nonce = nonce or urandom(50)

        derived_proof["nonce"] = bytes_to_b64(nonce,
                                              urlsafe=False,
                                              pad=True,
                                              encoding="utf-8")

        # Combine all the input statements that were originally signed
        # NOTE: we use plain strings here as input for the bbs lib.
        # the MATTR lib uses bytes, but the wrapper expects strings
        # it also works if we pass bytes as input
        all_input_statements = [*proof_statements, *document_statements]

        # Fetch the verification method
        verification_method = self._get_verification_method(
            proof=proof, document_loader=document_loader)

        # Create key pair from public key in verification method
        key_pair = self.key_pair.from_verification_method(verification_method)

        # Get the proof messages (revealed or not)
        proof_messages = []
        for input_statement_index in range(len(all_input_statements)):
            # if input statement index in revealed messages indexes use revealed type
            # otherwise use blinding
            proof_type = (ProofMessageType.Revealed
                          if input_statement_index in reveal_indices else
                          ProofMessageType.HiddenProofSpecificBlinding)
            proof_messages.append(
                ProofMessage(
                    message=all_input_statements[input_statement_index],
                    proof_type=proof_type,
                ))

        # get bbs key from bls key pair
        bbs_public_key = BlsKeyPair(
            public_key=key_pair.public_key).get_bbs_key(
                len(all_input_statements))

        # Compute the proof
        proof_request = CreateProofRequest(
            public_key=bbs_public_key,
            messages=proof_messages,
            signature=signature,
            nonce=nonce,
        )

        output_proof = bls_create_proof(proof_request)

        # Set the proof value on the derived proof
        derived_proof["proofValue"] = bytes_to_b64(output_proof,
                                                   urlsafe=False,
                                                   pad=True,
                                                   encoding="utf-8")

        # Set the relevant proof elements on the derived proof from the input proof
        derived_proof["verificationMethod"] = proof["verificationMethod"]
        derived_proof["proofPurpose"] = proof["proofPurpose"]
        derived_proof["created"] = proof["created"]

        return DeriveProofResult(document={**reveal_document_result},
                                 proof=derived_proof)
Beispiel #14
0
    async def verify_proof(
        self,
        *,
        proof: dict,
        document: dict,
        purpose: ProofPurpose,
        document_loader: DocumentLoaderMethod,
    ) -> ProofResult:
        """Verify proof against document and proof purpose."""
        try:
            proof["type"] = self.mapped_derived_proof_type

            # Get the proof and document statements
            proof_statements = self._create_verify_proof_data(
                proof=proof,
                document=document,
                document_loader=document_loader)
            document_statements = self._create_verify_document_data(
                document=document, document_loader=document_loader)

            # Transform the blank node identifier placeholders for the document statements
            # back into actual blank node identifiers
            transformed_document_statements = (
                self._transform_placeholder_node_ids_into_blank_node_ids(
                    document_statements))

            # Combine all the statements to be verified
            # NOTE: we use plain strings here as input for the bbs lib.
            # the MATTR lib uses bytes, but the wrapper expects strings
            # it also works if we pass bytes as input
            statements_to_verify = [
                *proof_statements, *transformed_document_statements
            ]

            # Fetch the verification method
            verification_method = self._get_verification_method(
                proof=proof, document_loader=document_loader)

            key_pair = self.key_pair.from_verification_method(
                verification_method)
            proof_bytes = b64_to_bytes(proof["proofValue"])

            total_message_count = get_total_message_count(proof_bytes)

            # get bbs key from bls key pair
            bbs_public_key = BlsKeyPair(public_key=key_pair.public_key
                                        ).get_bbs_key(total_message_count)

            # verify dervied proof
            verify_request = VerifyProofRequest(
                public_key=bbs_public_key,
                proof=proof_bytes,
                messages=statements_to_verify,
                nonce=b64_to_bytes(proof["nonce"]),
            )

            verified = bls_verify_proof(verify_request)

            if not verified:
                raise LinkedDataProofException(
                    f"Invalid signature on document {document}")

            purpose_result = purpose.validate(
                proof=proof,
                document=document,
                suite=self,
                verification_method=verification_method,
                document_loader=document_loader,
            )

            if not purpose_result.valid:
                return ProofResult(
                    verified=False,
                    purpose_result=purpose_result,
                    error=purpose_result.error,
                )

            return ProofResult(verified=True, purpose_result=purpose_result)
        except Exception as err:
            return ProofResult(verified=False, error=err)
Beispiel #15
0
 def test_get_public_g1_key_size(self):
     self.assertEqual(BlsKeyPair.public_g1_key_size(), 48,
                      "G1 key should be of length 48")
Beispiel #16
0
    def test_bbs_from_secret_key(self):
        secret_key = BlsKeyPair.generate_g2()
        public_key = secret_key.get_bbs_key(1)

        self.assertIsNotNone(public_key)
        self.assertEqual(196, len(public_key.public_key))
Beispiel #17
0
 def test_get_public_g2_key_size(self):
     self.assertEqual(BlsKeyPair.public_g2_key_size(), 96,
                      "G2 key should be of length 96")