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")
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
def unsecure_load(cls: Type[BaseSignedDataTypeVar], signed: bytes) -> BaseSignedDataTypeVar: """ Raises: DataError """ raw = VerifyKey.unsecure_unwrap(signed) return cls._deserialize(raw)
def unsecure_load(self, signed: bytes) -> "BaseSignedData": """ Raises: DataError """ raw = VerifyKey.unsecure_unwrap(signed) return self._deserialize(raw)
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}")
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}")
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], )
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)
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], )
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}")