diff --git a/lang/python/gpgme.i b/lang/python/gpgme.i index 5b3c193b..87fe90d2 100644 --- a/lang/python/gpgme.i +++ b/lang/python/gpgme.i @@ -255,11 +255,37 @@ PyObject* object_to_gpgme_t(PyObject* input, const char* objtype, int argnum) { $2 = $input; } -// Include the header file both for cc (first) and for swig (second) -// Include for swig locally since we need to fix 'class' usage there. -%{ -#include -%} +/* Include the unmodified for cc, and the cleaned-up local + version for SWIG. We do, however, want to hide certain fields on + some structs, which we provide prior to including the version for + SWIG. */ + %{ #include %} + +/* This is for notations, where we want to hide the length fields, and + the unused bit field block. */ +struct _gpgme_sig_notation +{ + struct _gpgme_sig_notation *next; + + /* If NAME is a null pointer, then VALUE contains a policy URL + rather than a notation. */ + char *name; + + /* The value of the notation data. */ + char *value; + + /* The accumulated flags. */ + gpgme_sig_notation_flags_t flags; + + /* Notation data is human-readable. */ + unsigned int human_readable : 1; + + /* Notation data is critical. */ + unsigned int critical : 1; +}; + +/* Now include our local modified version. Any structs defined above + are ignored. */ %include "gpgme.h" %include "errors.i" diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am index 0bc8c7f8..b85e82e3 100644 --- a/lang/python/tests/Makefile.am +++ b/lang/python/tests/Makefile.am @@ -38,6 +38,9 @@ py_tests = t-wrapper.py \ t-sign.py \ t-signers.py \ t-decrypt.py \ + t-verify.py \ + t-decrypt-verify.py \ + t-sig-notation.py \ t-export.py \ t-trustlist.py \ t-edit.py \ diff --git a/lang/python/tests/t-decrypt-verify.py b/lang/python/tests/t-decrypt-verify.py new file mode 100755 index 00000000..433e0a1e --- /dev/null +++ b/lang/python/tests/t-decrypt-verify.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 g10 Code GmbH +# +# This file is part of GPGME. +# +# GPGME is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# GPGME is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, see . + +from pyme import core, constants, errors +import support + +def check_verify_result(result, summary, fpr, status): + assert len(result.signatures) == 1, "Unexpected number of signatures" + sig = result.signatures[0] + assert sig.summary == summary, "Unexpected signature summary" + assert sig.fpr == fpr + assert errors.GPGMEError(sig.status).getcode() == status + assert len(sig.notations) == 0 + assert not sig.wrong_key_usage + assert sig.validity == constants.VALIDITY_UNKNOWN + assert errors.GPGMEError(sig.validity_reason).getcode() == errors.NO_ERROR + +support.init_gpgme(constants.PROTOCOL_OpenPGP) +c = core.Context() + +source = core.Data(file=support.make_filename("cipher-2.asc")) +sink = core.Data() + +c.op_decrypt_verify(source, sink) +result = c.op_decrypt_result() +assert not result.unsupported_algorithm, \ + "Unsupported algorithm: {}".format(result.unsupported_algorithm) + +support.print_data(sink) + +verify_result = c.op_verify_result() +check_verify_result(verify_result, 0, + "A0FF4590BB6122EDEF6E3C542D727CC768697734", + errors.NO_ERROR) diff --git a/lang/python/tests/t-sig-notation.py b/lang/python/tests/t-sig-notation.py new file mode 100755 index 00000000..2d832ef2 --- /dev/null +++ b/lang/python/tests/t-sig-notation.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 g10 Code GmbH +# +# This file is part of GPGME. +# +# GPGME is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# GPGME is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, see . + +import os +from pyme import core, constants, errors +import support + +expected_notations = { + "laughing@me": ("Just Squeeze Me", constants.SIG_NOTATION_HUMAN_READABLE), + "preferred-email-encoding@pgp.com": ("pgpmime", + constants.SIG_NOTATION_HUMAN_READABLE + | constants.SIG_NOTATION_CRITICAL), + None: ("http://www.gnu.org/policy/", 0), +} + +def check_result(result): + assert len(result.signatures) == 1, "Unexpected number of signatures" + sig = result.signatures[0] + assert len(sig.notations) == len(expected_notations) + + for r in sig.notations: + assert not 'name_len' in dir(r) + assert not 'value_len' in dir(r) + assert r.name in expected_notations + value, flags = expected_notations.pop(r.name) + + assert r.value == value, \ + "Expected {!r}, got {!r}".format(value, r.value) + assert r.human_readable \ + == bool(flags&constants.SIG_NOTATION_HUMAN_READABLE) + # xxx notyet + #assert r.human_readable \ + # == bool(flags&constants.SIG_NOTATION_CRITICAL) + + assert len(expected_notations) == 0 + +support.init_gpgme(constants.PROTOCOL_OpenPGP) + +source = core.Data("Hallo Leute\n") +signed = core.Data() + +c = core.Context() +for name, (value, flags) in expected_notations.items(): + c.sig_notation_add(name, value, flags) + +c.op_sign(source, signed, constants.SIG_MODE_NORMAL) + +signed.seek(0, os.SEEK_SET) +sink = core.Data() +c.op_verify(signed, None, sink) +result = c.op_verify_result() +check_result(result) diff --git a/lang/python/tests/t-verify.py b/lang/python/tests/t-verify.py new file mode 100755 index 00000000..333ee4e8 --- /dev/null +++ b/lang/python/tests/t-verify.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2016 g10 Code GmbH +# +# This file is part of GPGME. +# +# GPGME is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# GPGME is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, see . + +import os +from pyme import core, constants, errors +import support + +test_text1 = "Just GNU it!\n" +test_text1f= "Just GNU it?\n" +test_sig1 = """-----BEGIN PGP SIGNATURE----- + +iN0EABECAJ0FAjoS+i9FFIAAAAAAAwA5YmFyw7bDpMO8w58gZGFzIHdhcmVuIFVt +bGF1dGUgdW5kIGpldHp0IGVpbiBwcm96ZW50JS1aZWljaGVuNRSAAAAAAAgAJGZv +b2Jhci4xdGhpcyBpcyBhIG5vdGF0aW9uIGRhdGEgd2l0aCAyIGxpbmVzGhpodHRw +Oi8vd3d3Lmd1Lm9yZy9wb2xpY3kvAAoJEC1yfMdoaXc0JBIAoIiLlUsvpMDOyGEc +dADGKXF/Hcb+AKCJWPphZCphduxSvrzH0hgzHdeQaA== +=nts1 +-----END PGP SIGNATURE----- +""" + +test_sig2 = """-----BEGIN PGP MESSAGE----- + +owGbwMvMwCSoW1RzPCOz3IRxjXQSR0lqcYleSUWJTZOvjVdpcYmCu1+oQmaJIleH +GwuDIBMDGysTSIqBi1MApi+nlGGuwDeHao53HBr+FoVGP3xX+kvuu9fCMJvl6IOf +y1kvP4y+8D5a11ang0udywsA +=Crq6 +-----END PGP MESSAGE----- +""" + +# A message with a prepended but unsigned plaintext packet. +double_plaintext_sig = """-----BEGIN PGP MESSAGE----- + +rDRiCmZvb2Jhci50eHRF4pxNVGhpcyBpcyBteSBzbmVha3kgcGxhaW50ZXh0IG1l +c3NhZ2UKowGbwMvMwCSoW1RzPCOz3IRxTWISa6JebnG666MFD1wzSzJSixQ81XMV +UlITUxTyixRyKxXKE0uSMxQyEosVikvyCwpSU/S4FNCArq6Ce1F+aXJGvoJvYlGF +erFCTmJxiUJ5flFKMVeHGwuDIBMDGysTyA4GLk4BmO036xgWzMgzt9V85jCtfDFn +UqVooWlGXHwNw/xg/fVzt9VNbtjtJ/fhUqYo0/LyCGEA +=6+AK +-----END PGP MESSAGE----- +""" + +def check_result(result, summary, fpr, status, notation): + assert len(result.signatures) == 1, "Unexpected number of signatures" + sig = result.signatures[0] + assert sig.summary == summary, "Unexpected signature summary" + assert sig.fpr == fpr + assert errors.GPGMEError(sig.status).getcode() == status + + if notation: + expected_notations = { + "bar": b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f".decode() + + " das waren Umlaute und jetzt ein prozent%-Zeichen", + "foobar.1": "this is a notation data with 2 lines", + None: "http://www.gu.org/policy/", + } + assert len(sig.notations) == len(expected_notations) + + for r in sig.notations: + assert not 'name_len' in dir(r) + assert not 'value_len' in dir(r) + assert r.name in expected_notations + assert r.value == expected_notations[r.name], \ + "Expected {!r}, got {!r}".format(expected_notations[r.name], + r.value) + expected_notations.pop(r.name) + + assert len(expected_notations) == 0 + + assert not sig.wrong_key_usage + assert sig.validity == constants.VALIDITY_UNKNOWN + assert errors.GPGMEError(sig.validity_reason).getcode() == errors.NO_ERROR + + +support.init_gpgme(constants.PROTOCOL_OpenPGP) +c = core.Context() +c.set_armor(True) + +# Checking a valid message. +text = core.Data(test_text1) +sig = core.Data(test_sig1) +c.op_verify(sig, text, None) +result = c.op_verify_result() +check_result(result, 0, "A0FF4590BB6122EDEF6E3C542D727CC768697734", + errors.NO_ERROR, True) + + +# Checking a manipulated message. +text = core.Data(test_text1f) +sig.seek(0, os.SEEK_SET) +c.op_verify(sig, text, None) +result = c.op_verify_result() +check_result(result, constants.SIGSUM_RED, "2D727CC768697734", + errors.BAD_SIGNATURE, False) + +# Checking a normal signature. +text = core.Data() +sig = core.Data(test_sig2) +c.op_verify(sig, None, text) +result = c.op_verify_result() +check_result(result, 0, "A0FF4590BB6122EDEF6E3C542D727CC768697734", + errors.NO_ERROR, False) + +# Checking an invalid message. +text = core.Data() +sig = core.Data(double_plaintext_sig) +try: + c.op_verify(sig, None, text) +except Exception as e: + assert type(e) == errors.GPGMEError + assert e.getcode() == errors.BAD_DATA +else: + assert False, "Expected an error but got none."