diff options
Diffstat (limited to 'ffi/lang')
-rw-r--r-- | ffi/lang/python/.gitignore | 8 | ||||
-rw-r--r-- | ffi/lang/python/Makefile | 89 | ||||
-rw-r--r-- | ffi/lang/python/README.md | 5 | ||||
-rw-r--r-- | ffi/lang/python/examples/decrypt.py | 42 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/__init__.py | 8 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/core.py | 127 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/error.py | 69 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/glue.py | 127 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/net.py | 37 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/openpgp.py | 361 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/prelude.py | 6 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/sequoia_build.py | 60 | ||||
-rw-r--r-- | ffi/lang/python/sequoia/store.py | 243 | ||||
-rw-r--r-- | ffi/lang/python/setup.cfg | 2 | ||||
-rw-r--r-- | ffi/lang/python/setup.py | 41 | ||||
-rw-r--r-- | ffi/lang/python/tests/test_armor.py | 70 | ||||
-rw-r--r-- | ffi/lang/python/tests/test_cert.py | 75 | ||||
-rw-r--r-- | ffi/lang/python/tests/test_fingerprint.py | 45 | ||||
-rw-r--r-- | ffi/lang/python/tests/test_keyid.py | 63 | ||||
-rw-r--r-- | ffi/lang/python/tests/test_packet_parser.py | 53 | ||||
-rw-r--r-- | ffi/lang/python/tests/test_store.py | 47 |
21 files changed, 0 insertions, 1578 deletions
diff --git a/ffi/lang/python/.gitignore b/ffi/lang/python/.gitignore deleted file mode 100644 index 6d916e40..00000000 --- a/ffi/lang/python/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/build -/dist -/sequoia.egg-info -/.eggs -__pycache__ -*.pyc -.stamp* -_sequoia.*.so diff --git a/ffi/lang/python/Makefile b/ffi/lang/python/Makefile deleted file mode 100644 index e786fe35..00000000 --- a/ffi/lang/python/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# Makefile for Sequoia's Python bindings. - -# Configuration. -PREFIX ?= /usr/local -DESTDIR ?= -CFLAGS += -I../../include -I../../../openpgp-ffi/include - -# Tools. -PYTHON ?= python3 -IPYTHON ?= ipython3 -PYTEST ?= pytest-3 - -ifneq ($(filter Darwin %BSD,$(shell uname -s)),) - INSTALL ?= ginstall -else - INSTALL ?= install -endif - - -CARGO_TARGET_DIR ?= ../../../target - -ifneq "$(PYTHON)" "disable" -PY_VERSION = $(shell $(PYTHON) -c \ - 'import sys; print("{0.major}.{0.minor}".format(sys.version_info))') -endif - -# Make sure subprocesses pick these up. -export CFLAGS - -all: build - -.PHONY: build -build: .stamp-build -.stamp-build: sequoia/* ../../include/sequoia/* -ifneq "$(PYTHON)" "disable" - LDFLAGS=-L$(CARGO_TARGET_DIR)/debug $(PYTHON) setup.py build - touch $@ -endif - -# Testing and examples. -.PHONY: test check -test check: -ifneq "$(PYTHON)" "disable" - LDFLAGS=-L$(CARGO_TARGET_DIR)/debug LD_LIBRARY_PATH=$(CARGO_TARGET_DIR)/debug \ - $(PYTHON) setup.py test -endif - -.PHONY: shell -shell: build -ifneq "$(PYTHON)" "disable" - cp build/*/_sequoia.abi*.so . # XXX can we get setuptools to do that? - LDFLAGS=-L$(CARGO_TARGET_DIR)/debug LD_LIBRARY_PATH=$(CARGO_TARGET_DIR)/debug \ - $(IPYTHON) -i -c \ -'from sequoia.prelude import *; ctx = Context()' -endif - -# Installation. -.PHONY: build-release -build-release: .stamp-build-release -.stamp-build-release: -ifneq "$(PYTHON)" "disable" - rm -f .stamp-build - $(PYTHON) setup.py clean - LDFLAGS=-L$(CARGO_TARGET_DIR)/release \ - $(PYTHON) setup.py build - touch $@ -endif - -ifneq "$(DESTDIR)" "" - root_arg=--root=$(DESTDIR) -endif - -.PHONY: install -install: build-release -ifneq "$(PYTHON)" "disable" - $(INSTALL) -d $(DESTDIR)$(PREFIX)/lib/python$(PY_VERSION)/site-packages - - LDFLAGS=-L$(CARGO_TARGET_DIR)/release \ - $(PYTHON) setup.py install $(root_arg) --prefix=$(PREFIX) -endif - -# Housekeeping. -.PHONY: clean -clean: -ifneq "$(PYTHON)" "disable" - $(PYTHON) setup.py clean - rm -f _sequoia.*.so - rm -f .stamp-build .stamp-build-release -endif diff --git a/ffi/lang/python/README.md b/ffi/lang/python/README.md deleted file mode 100644 index 3be34691..00000000 --- a/ffi/lang/python/README.md +++ /dev/null @@ -1,5 +0,0 @@ -Python bindings for Sequoia -=========================== - -These bindings are work in progress. You are welcome to play with -them, but we will likely break the API in the future. diff --git a/ffi/lang/python/examples/decrypt.py b/ffi/lang/python/examples/decrypt.py deleted file mode 100644 index 1f4109e1..00000000 --- a/ffi/lang/python/examples/decrypt.py +++ /dev/null @@ -1,42 +0,0 @@ -import sys -import os -from getpass import getpass -from enum import Enum, auto -from sequoia.core import Context, NetworkPolicy -from sequoia.openpgp import Tag, PacketParser - -ctx = Context(network_policy=NetworkPolicy.Offline, - ephemeral=True) - -class State(Enum): - Start = auto() - Decrypted = auto() - Deciphered = auto() - Done = auto() - -state = State.Start -algo, key = None, None -pp = PacketParser.open(ctx, sys.argv[1]) -while pp.has_packet: - packet = pp.packet - tag = packet.tag - - if state == State.Start: - if tag == Tag.SKESK: - passphrase = getpass("Enter passphrase to decrypt message: ") - algo, key = packet.match().decrypt(passphrase.encode()) - state = State.Decrypted - elif tag == Tag.PKESK: - sys.stderr.write("Decryption using PKESK not yet supported.\n") - elif state == State.Decrypted: - if tag == Tag.SEIP: - pp.decrypt(algo, key) - state = State.Deciphered - elif state == State.Deciphered: - if tag == Tag.Literal: - body = pp.buffer_unread_content() - os.write(sys.stdout.fileno(), body) - state = State.Done - - pp.recurse() -assert state == State.Done diff --git a/ffi/lang/python/sequoia/__init__.py b/ffi/lang/python/sequoia/__init__.py deleted file mode 100644 index 4f84346e..00000000 --- a/ffi/lang/python/sequoia/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from . import ( - prelude, - error, - openpgp, - core, - net, - store, -) diff --git a/ffi/lang/python/sequoia/core.py b/ffi/lang/python/sequoia/core.py deleted file mode 100644 index b985247b..00000000 --- a/ffi/lang/python/sequoia/core.py +++ /dev/null @@ -1,127 +0,0 @@ -import io -from enum import Enum - -from _sequoia import ffi, lib -from .error import Error -from .glue import SQObject, invoke - -class NetworkPolicy(Enum): - Offline = lib.SQ_NETWORK_POLICY_OFFLINE - Anonymized = lib.SQ_NETWORK_POLICY_ANONYMIZED - Encrypted = lib.SQ_NETWORK_POLICY_ENCRYPTED - Insecure = lib.SQ_NETWORK_POLICY_INSECURE - -class IPCPolicy(Enum): - External = lib.SQ_IPC_POLICY_EXTERNAL - Internal = lib.SQ_IPC_POLICY_INTERNAL - Robust = lib.SQ_IPC_POLICY_ROBUST - -class Context(SQObject): - _del = lib.sq_context_free - def __init__(self, - home=None, - network_policy=NetworkPolicy.Encrypted, - ipc_policy=IPCPolicy.Robust, - ephemeral=False): - cfg = lib.sq_context_configure() - if home: - lib.sq_config_home(cfg, home.encode()) - lib.sq_config_network_policy(cfg, network_policy.value) - lib.sq_config_ipc_policy(cfg, ipc_policy.value) - if ephemeral: - lib.sq_config_ephemeral(cfg) - err = ffi.new("pgp_error_t[1]") - o = lib.sq_config_build(cfg, err) - if o == ffi.NULL: - raise Error._from(err[0]) - super(Context, self).__init__(o) - -class AbstractReader(SQObject, io.RawIOBase): - _del = lib.pgp_reader_free - - def readable(self): - return True - def writable(self): - return False - - def readinto(self, buf): - return invoke( - lib.pgp_reader_read, - self.ref(), - ffi.cast("uint8_t *", ffi.from_buffer(buf)), - len(buf)) - - def close(self): - self._delete() - - # Implement the context manager protocol. - def __enter__(self): - return self - def __exit__(self, *args): - self.close() - return False - -class Reader(AbstractReader): - @classmethod - def open(cls, ctx, filename): - return Reader( - invoke(lib.pgp_reader_from_file, - filename.encode()), - context=ctx) - - @classmethod - def from_fd(cls, ctx, fd): - return Reader(lib.pgp_reader_from_fd(fd), - context=ctx) - - @classmethod - def from_bytes(cls, ctx, buf): - return Reader( - lib.pgp_reader_from_bytes( - ffi.cast("uint8_t *", ffi.from_buffer(buf)), len(buf)), - context=ctx) - -class AbstractWriter(SQObject, io.RawIOBase): - _del = lib.pgp_writer_free - - def readable(self): - return False - def writable(self): - return True - - def write(self, buf): - return invoke( - lib.pgp_writer_write, - self.ref(), - ffi.cast("const uint8_t *", ffi.from_buffer(buf)), - len(buf)) - - def close(self): - self._delete() - - # Implement the context manager protocol. - def __enter__(self): - return self - def __exit__(self, *args): - self.close() - return False - -class Writer(AbstractWriter): - @classmethod - def open(cls, ctx, filename): - return Writer( - invoke(lib.pgp_writer_from_file, - filename.encode()), - context=ctx) - - @classmethod - def from_fd(cls, ctx, fd): - return Writer(lib.pgp_writer_from_fd(fd), - context=ctx) - - @classmethod - def from_bytes(cls, ctx, buf): - return Writer( - lib.pgp_writer_from_bytes( - ffi.cast("uint8_t *", ffi.from_buffer(buf)), len(buf)), - context=ctx) diff --git a/ffi/lang/python/sequoia/error.py b/ffi/lang/python/sequoia/error.py deleted file mode 100644 index 0b434801..00000000 --- a/ffi/lang/python/sequoia/error.py +++ /dev/null @@ -1,69 +0,0 @@ -from _sequoia import ffi, lib -from .glue import sq_str - -class Error(Exception): - @classmethod - def _from(cls, o): - if o == ffi.NULL: - return MalformedValue() - - status = lib.pgp_error_status(o) - return _status_map[status](o) - - @classmethod - def _last(cls, ctx): - if not ctx: - return MalformedValue() - return Error._from(lib.sq_context_last_error(ctx.ref())) - -class MalformedValue(Error, ValueError): - def __init__(self, message="Malformed value"): - super(MalformedValue, self).__init__(message) - -class SQError(Error): - def __init__(self, o): - self.__o = ffi.gc(o, lib.pgp_error_free) - super(SQError, self).__init__(sq_str(lib.pgp_error_to_string(self.__o))) - -class Success(SQError): - pass - -class UnknownError(SQError): - pass - -class NetworkPolicyViolation(SQError): - pass - -class IoError(SQError): - pass - -class InvalidOperataion(SQError): - pass - -class MalformedPacket(SQError): - pass - -class UnsupportedHashAlgorithm(SQError): - pass - -class UnsupportedSymmetricAlgorithm(SQError): - pass - -class InvalidPassword(SQError): - pass - -class InvalidSessionKey(SQError): - pass - -_status_map = { - lib.PGP_STATUS_SUCCESS: Success, - lib.PGP_STATUS_UNKNOWN_ERROR: UnknownError, - lib.PGP_STATUS_NETWORK_POLICY_VIOLATION: NetworkPolicyViolation, - lib.PGP_STATUS_IO_ERROR: IoError, - lib.PGP_STATUS_INVALID_OPERATION: InvalidOperataion, - lib.PGP_STATUS_MALFORMED_PACKET: MalformedPacket, - lib.PGP_STATUS_UNSUPPORTED_HASH_ALGORITHM: UnsupportedHashAlgorithm, - lib.PGP_STATUS_UNSUPPORTED_SYMMETRIC_ALGORITHM: UnsupportedSymmetricAlgorithm, - lib.PGP_STATUS_INVALID_PASSWORD: InvalidPassword, - lib.PGP_STATUS_INVALID_SESSION_KEY: InvalidSessionKey, -} diff --git a/ffi/lang/python/sequoia/glue.py b/ffi/lang/python/sequoia/glue.py deleted file mode 100644 index cca3a71d..00000000 --- a/ffi/lang/python/sequoia/glue.py +++ /dev/null @@ -1,127 +0,0 @@ -from datetime import datetime, timezone - -from _sequoia import ffi, lib -from . import error - -def invoke(fun, *args): - """Invokes the given FFI function. - - This function invokes the given FFI function. It must only be - used for functions that expect an error pointer as first argument. - - If an error is encountered, an exception is raised. - """ - err = ffi.new("pgp_error_t[1]") - result = fun(err, *args) - if err[0] != ffi.NULL: - raise Error._from(err[0]) - return result - -class SQObject(object): - # These class attributes determine what features the wrapper class - # implements. They must be set to the relevant Sequoia functions. - # - # XXX: Once we can assume Python3.6 we can use '__init_subclass__' - # and reflection on the 'lib' object to set them automatically - # using the type name. - _del = None - _clone = None - _eq = None - _str = None - _debug = None - _hash = None - - def __init__(self, o, context=None, owner=None, references=None): - if o == ffi.NULL: - raise error.Error._last(context) - self.__o = None - self.ref_replace(o, owner=owner, references=references) - self.__ctx = context - if self.__class__._hash is None and not hasattr(self.__class__, '__hash__'): - # Unhashable types must have '__hash__' set to None. - # Until we can use '__init_subclass__', we need to patch - # the class here. Yuck. - self.__class__.__hash__ = None - - def ref(self): - return self.__o - - def ref_consume(self): - ref = self.ref() - self._delete(skip_free=True) - return ref - - def ref_replace(self, new, owner=None, references=None): - old = self.ref_consume() - if self._del and owner == None: - # There is a destructor and We own the referenced object - # new. - self.__o = ffi.gc(new, self._del) - else: - self.__o = new - self.__owner = owner - self.__references = references - return old - - def _delete(self, skip_free=False): - if not self.__o: - return - if self._del and skip_free: - ffi.gc(self.__o, None) - self.__o = None - self.__owner = None - self.__references = None - - def context(self): - return self.__ctx - - def __str__(self): - if self._str: - return _str(self._str(self.ref())) - else: - return repr(self) - - def __eq__(self, other): - if self._eq: - return (isinstance(other, self.__class__) - and bool(self._eq(self.ref(), other.ref()))) - else: - return NotImplemented - - def copy(self): - if self._clone: - return self.__class__(self._clone(self.ref())) - else: - raise NotImplementedError() - - def __hash__(self): - return self._hash(self.ref()) - - def debug(self): - if self._debug: - return _str(self._debug(self.ref())) - else: - raise NotImplementedError() - -def sq_str(s): - t = ffi.string(s).decode() - lib.free(s) - return t -_str = sq_str - -def sq_static_str(s): - return ffi.string(s).decode() -_static_str = sq_static_str - -def sq_iterator(iterator, next_fn, map=lambda x: x): - while True: - entry = next_fn(iterator) - if entry == ffi.NULL: - break - yield map(entry) - -def sq_time(t): - if t == 0: - return None - else: - return datetime.fromtimestamp(t, timezone.utc) diff --git a/ffi/lang/python/sequoia/net.py b/ffi/lang/python/sequoia/net.py deleted file mode 100644 index de7a77fc..00000000 --- a/ffi/lang/python/sequoia/net.py +++ /dev/null @@ -1,37 +0,0 @@ -from _sequoia import ffi, lib - -from .openpgp import Cert -from .error import Error -from .glue import SQObject - -class KeyServer(SQObject): - _del = lib.sq_keyserver_free - - @classmethod - def new(cls, ctx, uri, cert=None): - if not cert: - ks = lib.sq_keyserver_new(ctx.ref(), uri.encode()) - else: - ks = lib.sq_keyserver_with_cert( - ctx.ref(), uri.encode(), - ffi.cast("uint8_t *", ffi.from_buffer(cert)), - len(cert)) - return KeyServer(ks, context=ctx) - - @classmethod - def keys_openpgp_org(cls, ctx): - return KeyServer(lib.sq_keyserver_keys_openpgp_org(ctx.ref()), - context=ctx) - - def get(self, keyid): - return Cert(lib.sq_keyserver_get(self.context().ref(), - self.ref(), - keyid.ref()), - context=self.context()) - - def send(self, cert): - r = lib.sq_keyserver_send(self.context().ref(), - self.ref(), - cert.ref()) - if r: - raise Error._last(self.context()) diff --git a/ffi/lang/python/sequoia/openpgp.py b/ffi/lang/python/sequoia/openpgp.py deleted file mode 100644 index a2b8e1fd..00000000 --- a/ffi/lang/python/sequoia/openpgp.py +++ /dev/null @@ -1,361 +0,0 @@ -from enum import Enum - -from _sequoia import ffi, lib -from .error import Error -from .glue import _str, SQObject, invoke -from .core import AbstractReader, AbstractWriter - -class KeyID(SQObject): - _del = lib.pgp_keyid_free - _clone = lib.pgp_keyid_clone - _str = lib.pgp_keyid_to_string - _debug = lib.pgp_keyid_debug - _eq = lib.pgp_keyid_equal - _hash = lib.pgp_keyid_hash - - @classmethod - def from_bytes(cls, fp): - if len(fp) != 8: - raise Error("KeyID must be of length 8") - return KeyID(lib.pgp_keyid_from_bytes( - ffi.cast("uint8_t *", ffi.from_buffer(fp)))) - - @classmethod - def from_hex(cls, fp): - return KeyID(lib.pgp_keyid_from_hex(fp.encode())) - - def hex(self): - return _str(lib.pgp_keyid_to_hex(self.ref())) - -class Fingerprint(SQObject): - _del = lib.pgp_fingerprint_free - _clone = lib.pgp_fingerprint_clone - _str = lib.pgp_fingerprint_to_string - _debug = lib.pgp_fingerprint_debug - _eq = lib.pgp_fingerprint_equal - _hash = lib.pgp_fingerprint_hash - - @classmethod - def from_bytes(cls, fp): - return Fingerprint(lib.pgp_fingerprint_from_bytes( - ffi.cast("uint8_t *", ffi.from_buffer(fp)), len(fp))) - - @classmethod - def from_hex(cls, fp): - return Fingerprint(lib.pgp_fingerprint_from_hex(fp.encode())) - - def hex(self): - return _str(lib.pgp_fingerprint_to_hex(self.ref())) - - def keyid(self): - return KeyID(lib.pgp_fingerprint_to_keyid(self.ref())) - -class PacketPile(SQObject): - _debug = lib.pgp_packet_pile_debug - _del = lib.pgp_packet_pile_free - _clone = lib.pgp_packet_pile_clone - - @classmethod - def from_reader(cls, ctx, reader): - return PacketPile(invoke(lib.pgp_packet_pile_from_reader, reader.ref()), - context=ctx) - - @classmethod - def open(cls, ctx, filename): - return PacketPile(invoke(lib.pgp_packet_pile_from_file, filename.encode()), - context=ctx) - - @classmethod - def from_bytes(cls, ctx, source): - return PacketPile(invoke(lib.pgp_packet_pile_from_bytes, - ffi.from_buffer(source), - len(source)), - context=ctx) - - def serialize(self, writer): - status = invoke(lib.pgp_packet_pile_serialize, - self.ref(), - writer.ref()) - if status: - raise Error._last(self.context()) - -class Cert(SQObject): - _del = lib.pgp_cert_free - _clone = lib.pgp_cert_clone - _eq = lib.pgp_cert_equal - _str = lib.pgp_cert_to_string - _debug = lib.pgp_cert_debug - - @classmethod - def from_reader(cls, ctx, reader): - return Cert(invoke(lib.pgp_cert_from_reader, reader.ref()), - context=ctx) - - @classmethod - def open(cls, ctx, filename): - return Cert(invoke(lib.pgp_cert_from_file, filename.encode()), - context=ctx) - - @classmethod - def from_packet_pile(cls, ctx, packet_pile): - return Cert(invoke(lib.pgp_cert_from_packet_pile, packet_pile.ref_consume()), - context=ctx) - - @classmethod - def from_bytes(cls, ctx, source): - return Cert(invoke(lib.pgp_cert_from_bytes, - ffi.from_buffer(source), - len(source)), - context=ctx) - - def serialize(self, writer): - status = invoke(lib.pgp_cert_serialize, - self.ref(), - writer.ref()) - if status: - raise Error._last(self.context()) - - def fingerprint(self): - return Fingerprint(lib.pgp_cert_fingerprint(self.ref()), - context=self.context()) - - def merge(self, other): - new = invoke(lib.pgp_cert_merge, - self.ref_consume(), - other.ref_consume()) - if new == ffi.NULL: - raise Error._last(self.context()) - self.ref_replace(new) - -class Kind(Enum): - Message = lib.PGP_ARMOR_KIND_MESSAGE - PublicKey = lib.PGP_ARMOR_KIND_PUBLICKEY - SecretKey = lib.PGP_ARMOR_KIND_SECRETKEY - Signature = lib.PGP_ARMOR_KIND_SIGNATURE - File = lib.PGP_ARMOR_KIND_FILE - Any = lib.PGP_ARMOR_KIND_ANY - -class ArmorReader(AbstractReader): - @classmethod - def new(cls, ctx, inner, kind=Kind.Any): - ar = ArmorReader(lib.pgp_armor_reader_new(inner.ref(), - kind.value), - context=ctx) - ar.inner = inner - return ar - - def close(self): - super(ArmorReader, self)._delete() - self.inner.close() - -class ArmorWriter(AbstractWriter): - @classmethod - def new(cls, ctx, inner, kind): - aw = ArmorWriter(invoke(lib.pgp_armor_writer_new, - inner.ref(), - kind.value, - ffi.NULL, 0), #XXX headers - context=ctx) - aw.inner = inner - return aw - - def close(self): - in |