summaryrefslogtreecommitdiffstats
path: root/ffi/lang
diff options
context:
space:
mode:
Diffstat (limited to 'ffi/lang')
-rw-r--r--ffi/lang/python/.gitignore8
-rw-r--r--ffi/lang/python/Makefile89
-rw-r--r--ffi/lang/python/README.md5
-rw-r--r--ffi/lang/python/examples/decrypt.py42
-rw-r--r--ffi/lang/python/sequoia/__init__.py8
-rw-r--r--ffi/lang/python/sequoia/core.py127
-rw-r--r--ffi/lang/python/sequoia/error.py69
-rw-r--r--ffi/lang/python/sequoia/glue.py127
-rw-r--r--ffi/lang/python/sequoia/net.py37
-rw-r--r--ffi/lang/python/sequoia/openpgp.py361
-rw-r--r--ffi/lang/python/sequoia/prelude.py6
-rw-r--r--ffi/lang/python/sequoia/sequoia_build.py60
-rw-r--r--ffi/lang/python/sequoia/store.py243
-rw-r--r--ffi/lang/python/setup.cfg2
-rw-r--r--ffi/lang/python/setup.py41
-rw-r--r--ffi/lang/python/tests/test_armor.py70
-rw-r--r--ffi/lang/python/tests/test_cert.py75
-rw-r--r--ffi/lang/python/tests/test_fingerprint.py45
-rw-r--r--ffi/lang/python/tests/test_keyid.py63
-rw-r--r--ffi/lang/python/tests/test_packet_parser.py53
-rw-r--r--ffi/lang/python/tests/test_store.py47
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