Merge pull request #86 from matrix-org/add_python_pk_signing
add python bindings for PK signing
This commit is contained in:
commit
0d0169c839
3 changed files with 122 additions and 4 deletions
|
@ -40,7 +40,9 @@ from .pk import (
|
|||
PkMessage,
|
||||
PkEncryption,
|
||||
PkDecryption,
|
||||
PkSigning,
|
||||
PkEncryptionError,
|
||||
PkDecryptionError
|
||||
PkDecryptionError,
|
||||
PkSigningError
|
||||
)
|
||||
from .sas import Sas, OlmSasError
|
||||
|
|
112
python/olm/pk.py
112
python/olm/pk.py
|
@ -17,7 +17,8 @@
|
|||
|
||||
This module contains bindings to the PK part of the Olm library.
|
||||
It contains two classes PkDecryption and PkEncryption that are used to
|
||||
establish an encrypted communication channel using public key encryption.
|
||||
establish an encrypted communication channel using public key encryption,
|
||||
as well as a class PkSigning that is used to sign a message.
|
||||
|
||||
Examples:
|
||||
>>> decryption = PkDecryption()
|
||||
|
@ -25,16 +26,22 @@ Examples:
|
|||
>>> plaintext = "It's a secret to everybody."
|
||||
>>> message = encryption.encrypt(plaintext)
|
||||
>>> decrypted_plaintext = decryption.decrypt(message)
|
||||
>>> seed = PkSigning.generate_seed()
|
||||
>>> signing = PkSigning(seed)
|
||||
>>> signature = signing.sign(plaintext)
|
||||
>>> ed25519_verify(signing.public_key, plaintext, signature)
|
||||
|
||||
"""
|
||||
|
||||
from builtins import super
|
||||
from typing import AnyStr, Type
|
||||
|
||||
from future.utils import bytes_to_native_str
|
||||
|
||||
from _libolm import ffi, lib # type: ignore
|
||||
from ._finalize import track_for_finalization
|
||||
|
||||
from ._compat import URANDOM, to_bytearray
|
||||
from ._finalize import track_for_finalization
|
||||
|
||||
|
||||
class PkEncryptionError(Exception):
|
||||
|
@ -45,6 +52,10 @@ class PkDecryptionError(Exception):
|
|||
"""libolm Pk decryption exception."""
|
||||
|
||||
|
||||
class PkSigningError(Exception):
|
||||
"""libolm Pk signing exception."""
|
||||
|
||||
|
||||
def _clear_pk_encryption(pk_struct):
|
||||
lib.olm_clear_pk_encryption(pk_struct)
|
||||
|
||||
|
@ -344,3 +355,100 @@ class PkDecryption(object):
|
|||
lib.memset(plaintext_buffer, 0, max_plaintext_length)
|
||||
|
||||
return bytes_to_native_str(plaintext)
|
||||
|
||||
|
||||
def _clear_pk_signing(pk_struct):
|
||||
lib.olm_clear_pk_signing(pk_struct)
|
||||
|
||||
|
||||
class PkSigning(object):
|
||||
"""PkSigning class.
|
||||
|
||||
Signs messages using public key cryptography.
|
||||
|
||||
Attributes:
|
||||
public_key (str): The public key of the PkSigning object, can be
|
||||
shared and used to verify using Utility.ed25519_verify.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, seed):
|
||||
# type: (bytes) -> None
|
||||
"""Create a new signing object.
|
||||
|
||||
Args:
|
||||
seed(bytes): the seed to use as the private key for signing. The
|
||||
seed must have the same length as the seeds generated by
|
||||
PkSigning.generate_seed().
|
||||
"""
|
||||
if not seed:
|
||||
raise ValueError("seed can't be empty")
|
||||
|
||||
self._buf = ffi.new("char[]", lib.olm_pk_signing_size())
|
||||
self._pk_signing = lib.olm_pk_signing(self._buf)
|
||||
track_for_finalization(self, self._pk_signing, _clear_pk_signing)
|
||||
|
||||
seed_buffer = ffi.new("char[]", seed)
|
||||
|
||||
pubkey_length = lib.olm_pk_signing_public_key_length()
|
||||
pubkey_buffer = ffi.new("char[]", pubkey_length)
|
||||
|
||||
ret = lib.olm_pk_signing_key_from_seed(
|
||||
self._pk_signing,
|
||||
pubkey_buffer, pubkey_length,
|
||||
seed_buffer, len(seed)
|
||||
)
|
||||
|
||||
# zero out copies of the seed
|
||||
lib.memset(seed_buffer, 0, len(seed))
|
||||
|
||||
self._check_error(ret)
|
||||
|
||||
self.public_key = bytes_to_native_str(
|
||||
ffi.unpack(pubkey_buffer, pubkey_length)
|
||||
)
|
||||
|
||||
def _check_error(self, ret):
|
||||
# type: (int) -> None
|
||||
if ret != lib.olm_error():
|
||||
return
|
||||
|
||||
last_error = bytes_to_native_str(
|
||||
ffi.string(lib.olm_pk_signing_last_error(self._pk_signing)))
|
||||
|
||||
raise PkSigningError(last_error)
|
||||
|
||||
@classmethod
|
||||
def generate_seed(cls):
|
||||
# type: () -> bytes
|
||||
"""Generate a random seed.
|
||||
"""
|
||||
random_length = lib.olm_pk_signing_seed_length()
|
||||
random = URANDOM(random_length)
|
||||
|
||||
return random
|
||||
|
||||
def sign(self, message):
|
||||
# type: (AnyStr) -> str
|
||||
"""Sign a message
|
||||
|
||||
Returns the signature.
|
||||
Raises PkSigningError on failure.
|
||||
|
||||
Args:
|
||||
message(str): the message to sign.
|
||||
"""
|
||||
bytes_message = to_bytearray(message)
|
||||
|
||||
signature_length = lib.olm_pk_signature_length()
|
||||
signature_buffer = ffi.new("char[]", signature_length)
|
||||
|
||||
ret = lib.olm_pk_sign(
|
||||
self._pk_signing,
|
||||
ffi.from_buffer(bytes_message), len(bytes_message),
|
||||
signature_buffer, signature_length)
|
||||
self._check_error(ret)
|
||||
|
||||
return bytes_to_native_str(
|
||||
ffi.unpack(signature_buffer, signature_length)
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from olm import PkDecryption, PkDecryptionError, PkEncryption
|
||||
from olm import (PkDecryption, PkDecryptionError, PkEncryption, PkSigning,
|
||||
ed25519_verify)
|
||||
|
||||
|
||||
class TestClass(object):
|
||||
|
@ -47,3 +48,10 @@ class TestClass(object):
|
|||
|
||||
with pytest.raises(PkDecryptionError):
|
||||
PkDecryption.from_pickle(pickle, "Not secret")
|
||||
|
||||
def test_signing(self):
|
||||
seed = PkSigning.generate_seed()
|
||||
signing = PkSigning(seed)
|
||||
message = "This statement is true"
|
||||
signature = signing.sign(message)
|
||||
ed25519_verify(signing.public_key, message, signature)
|
||||
|
|
Loading…
Reference in a new issue