def test_signing_key():
    from parsec.crypto import (
        _PySigningKey,
        _RsSigningKey,
        SigningKey,
        VerifyKey,
        _RsVerifyKey,
        _PyVerifyKey,
    )

    assert SigningKey is _RsSigningKey
    assert VerifyKey is _RsVerifyKey

    KEY = b"a" * 32

    rs_sk = SigningKey(KEY)
    py_sk = _PySigningKey(KEY)

    rs_vk = rs_sk.verify_key
    py_vk = py_sk.verify_key

    assert SigningKey(KEY) == SigningKey(KEY)
    assert SigningKey(KEY) != SigningKey(b"b" * 32)

    assert isinstance(rs_vk, _RsVerifyKey)
    assert isinstance(py_vk, _PyVerifyKey)

    # Sign a message with both, check if the signed message is the same
    MESSAGE = b"My message"

    rs_signed = rs_sk.sign(MESSAGE)
    py_signed = py_sk.sign(MESSAGE)

    assert rs_signed == py_signed

    # Verify with both
    assert rs_vk.verify(rs_signed) == py_vk.verify(py_signed)
    assert rs_vk.verify(py_signed) == py_vk.verify(rs_signed)

    # Check if unsecure_unwrap is the same
    assert VerifyKey.unsecure_unwrap(
        rs_signed) == _PyVerifyKey.unsecure_unwrap(py_signed)
    assert VerifyKey.unsecure_unwrap(
        py_signed) == _PyVerifyKey.unsecure_unwrap(rs_signed)

    # Check if generate returns the right type
    assert isinstance(SigningKey.generate(), SigningKey)
    assert isinstance(_PySigningKey.generate(), _PySigningKey)

    # Check if they both react in a similar manner with incorrect data
    assert rs_vk.unsecure_unwrap(b"random_data") == py_vk.unsecure_unwrap(
        b"random_data")

    with pytest.raises(nacl.exceptions.CryptoError):
        py_vk.verify(b"random_data")
    with pytest.raises(nacl.exceptions.CryptoError):
        rs_vk.verify(b"random data")
예제 #2
0
    def verify_and_load(
        cls: Type[BaseSignedDataTypeVar],
        signed: bytes,
        author_verify_key: VerifyKey,
        expected_author: Optional[DeviceID],
        expected_timestamp: Optional[DateTime] = None,
        **kwargs,
    ) -> BaseSignedDataTypeVar:
        """
        Raises:
            DataError
        """
        try:
            raw = author_verify_key.verify(signed)

        except CryptoError as exc:
            raise DataError(str(exc)) from exc

        data = cls._deserialize(raw)
        if data.author != expected_author:
            repr_expected_author = expected_author or "<root key>"
            raise DataError(
                f"Invalid author: expected `{repr_expected_author}`, got `{data.author}`"
            )
        if expected_timestamp is not None and data.timestamp != expected_timestamp:
            raise DataError(
                f"Invalid timestamp: expected `{expected_timestamp}`, got `{data.timestamp}`"
            )
        return data
예제 #3
0
 def unsecure_load(cls: Type[BaseSignedDataTypeVar], signed: bytes) -> BaseSignedDataTypeVar:
     """
     Raises:
         DataError
     """
     raw = VerifyKey.unsecure_unwrap(signed)
     return cls._deserialize(raw)
예제 #4
0
 def unsecure_load(self, signed: bytes) -> "BaseSignedData":
     """
     Raises:
         DataError
     """
     raw = VerifyKey.unsecure_unwrap(signed)
     return self._deserialize(raw)
예제 #5
0
    async def bootstrap(
        self,
        id: OrganizationID,
        user: User,
        first_device: Device,
        bootstrap_token: str,
        root_verify_key: VerifyKey,
    ) -> None:
        async with self.dbh.pool.acquire() as conn, conn.transaction():
            organization = await self._get(conn, id)

            if organization.is_bootstrapped():
                raise OrganizationAlreadyBootstrappedError()

            if organization.bootstrap_token != bootstrap_token:
                raise OrganizationInvalidBootstrapTokenError()

            try:
                await _create_user(conn, id, user, first_device)
            except UserError as exc:
                raise OrganizationFirstUserCreationError(exc) from exc

            result = await conn.execute(*_q_bootstrap_organization(
                organization_id=id,
                bootstrap_token=bootstrap_token,
                root_verify_key=root_verify_key.encode(),
            ))

            if result != "UPDATE 1":
                raise OrganizationError(f"Update error: {result}")
예제 #6
0
    async def bootstrap(
        self,
        id: OrganizationID,
        user: User,
        first_device: Device,
        bootstrap_token: str,
        root_verify_key: VerifyKey,
    ) -> None:
        async with self.dbh.pool.acquire() as conn, conn.transaction():
            # The FOR UPDATE in the query ensure the line is locked in the
            # organization table until the end of the transaction. Hence
            # preventing concurrent bootstrap operation form going any further.
            organization = await self._get(conn, id, for_update=True)

            if organization.is_bootstrapped():
                raise OrganizationAlreadyBootstrappedError()

            if organization.bootstrap_token != bootstrap_token:
                raise OrganizationInvalidBootstrapTokenError()

            try:
                await q_create_user(conn, id, user, first_device)
            except UserError as exc:
                raise OrganizationFirstUserCreationError(exc) from exc

            result = await conn.execute(*_q_bootstrap_organization(
                organization_id=id.str,
                bootstrap_token=bootstrap_token,
                root_verify_key=root_verify_key.encode(),
            ))

            if result != "UPDATE 1":
                raise OrganizationError(f"Update error: {result}")
예제 #7
0
    async def _get(conn, id: OrganizationID) -> Organization:
        data = await conn.fetchrow(*_q_get_organization(organization_id=id))
        if not data:
            raise OrganizationNotFoundError()

        rvk = VerifyKey(data[1]) if data[1] else None
        return Organization(
            organization_id=id,
            bootstrap_token=data[0],
            root_verify_key=rvk,
            expiration_date=data[2],
        )
예제 #8
0
    async def _get(conn, id: OrganizationID) -> Organization:
        data = await conn.fetchrow(
            """
                SELECT bootstrap_token, root_verify_key
                FROM organizations WHERE organization_id = $1
                """,
            id,
        )
        if not data:
            raise OrganizationNotFoundError()

        rvk = VerifyKey(data[1]) if data[1] else None
        return Organization(organization_id=id, bootstrap_token=data[0], root_verify_key=rvk)
예제 #9
0
    async def _get(conn,
                   id: OrganizationID,
                   for_update: bool = False) -> Organization:
        if for_update:
            data = await conn.fetchrow(*_q_get_organization_for_update(
                organization_id=id.str))
        else:
            data = await conn.fetchrow(*_q_get_organization(
                organization_id=id.str))
        if not data:
            raise OrganizationNotFoundError()

        rvk = VerifyKey(data[1]) if data[1] else None
        return Organization(
            organization_id=id,
            bootstrap_token=data[0],
            root_verify_key=rvk,
            is_expired=data[2],
            active_users_limit=data[3],
            user_profile_outsider_allowed=data[4],
        )
예제 #10
0
    async def bootstrap(
        self,
        organization_id: OrganizationID,
        user: User,
        bootstrap_token: str,
        root_verify_key: VerifyKey,
    ) -> None:
        async with self.dbh.pool.acquire() as conn:
            async with conn.transaction():
                organization = await self._get(conn, organization_id)

                if organization.is_bootstrapped():
                    raise OrganizationAlreadyBootstrappedError()

                if organization.bootstrap_token != bootstrap_token:
                    raise OrganizationInvalidBootstrapTokenError()

                try:
                    await self.user_component._create_user(conn, organization_id, user)
                except UserError as exc:
                    raise OrganizationFirstUserCreationError(exc) from exc

                result = await conn.execute(
                    """
                    UPDATE organizations
                    SET root_verify_key = $3
                    WHERE organization_id = $1
                        AND bootstrap_token = $2
                        AND root_verify_key IS NULL;
                    """,
                    organization_id,
                    bootstrap_token,
                    root_verify_key.encode(),
                )

                if result != "UPDATE 1":
                    raise OrganizationError(f"Update error: {result}")