File add-sexp-pk.diff of Package perl-Crypt-GCrypt

--- ./GCrypt.xs.orig	2022-06-23 11:11:43.790179037 +0000
+++ ./GCrypt.xs	2022-06-23 11:13:33.709971709 +0000
@@ -75,6 +75,7 @@ typedef struct Crypt_GCrypt_s *Crypt_GCr
 
 /* a Crypt_GCrypt_MPI need only be a pointer to a gcrypt MPI object */
 typedef gcry_mpi_t Crypt_GCrypt_MPI;
+typedef gcry_sexp_t Crypt_GCrypt_Sexp;
 
 /* return the offset of padding or -1 if none */
 int find_padding (Crypt_GCrypt gcr, unsigned char *string, size_t string_len) {
@@ -178,6 +179,197 @@ init_library() {
   gcry_control(GCRYCTL_INITIALIZATION_FINISHED);
 }
 
+static void
+free_sexp_build_array(void **array)
+{
+    if (array) {
+	void **a = array;
+	for (; *a; a++)
+	    free(*a);
+	free((void *)array);
+    }
+}
+
+static const char *
+skip_sexp_format_quoted(const char *format)
+{
+    int c;
+    while ((c = *format++) != 0) {
+	if (c == '\\') {
+	    c = *format++;
+	    if (c >= '0' && c <= '7') {
+		if (!(format[0] >= 0 && format[0] <= '7' && format[1] >= 0 && format[1] <= '7'))
+		    return NULL;
+		format += 2;
+	    } else if (c == 'x') {
+		c = format[0];
+		if (!((c >= '0' && c <= '0') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
+		    return NULL;
+		c = format[1];
+		if (!((c >= '0' && c <= '0') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
+		    return NULL;
+		format += 2;
+	    } else if (c == '\r') {
+		if (format[0] == '\n')
+		    format++;
+	    } else if (c == '\n') {
+		if (format[0] == '\r')
+		    format++;
+	    } else if (strchr("btvnfr\"'\\", c)) {
+		continue;
+	    } else {
+		return NULL;
+	    }
+	} else if (c == '\"') {
+	    return format;
+	}
+    }
+    return NULL;
+}
+
+static const char *
+skip_sexp_format_hex(const char *format)
+{
+    int c;
+    int hexcount = 0;
+    while ((c = *format++) != 0) {
+	if (c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n')
+	    continue;
+	if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+	    hexcount++;
+	    continue;
+	}
+	if (c != '#' || (hexcount & 1) != 0)
+	    return NULL;
+	return format;
+    }
+    return NULL;
+}
+
+static const char *
+skip_sexp_format_base64(const char *format)
+{
+    int c;
+    while ((c = *format++) != 0) {
+	if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '+' || c == '/' || c == '=')
+	    continue;
+	if (c != '|')
+	    return NULL;
+	return format;
+    }
+    return NULL;
+}
+
+static const char *
+skip_sexp_format_token(const char *format)
+{
+    int c;
+    while ((c = *format++) != 0) {
+	if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || strchr("-./_:*+=", c))
+	    continue;
+	return format - 1;
+    }
+    return format - 1;
+}
+
+static void **
+create_sexp_build_array(const char *format, SV **args, I32 nargs)
+{
+    int c;
+    void **arr = NULL;
+    int narr = 0;
+    while (format && (c = *format++) != 0) {
+	if (c == '\"')
+	    format = skip_sexp_format_quoted(format);
+	else if (c == '#')
+	    format = skip_sexp_format_hex(format);
+	else if (c == '|')
+	    format = skip_sexp_format_base64(format);
+	else if (c >= '0' && c <= '9') {
+	    const char *f = format;
+	    while (*f >= '0' && *f <= '9')
+		f++;
+	    if (*f == ':' && f - format < 10) {
+		int l = atoi(format);
+		format = f + 1;
+		if (l < 0 || l > strlen(format)) {
+		    format = 0;
+		} else {
+		    format += l;
+		}
+	    } else if (*f == '\"' || *f == '#' || *f == '|') {
+		format = f;
+	    } else {
+		format = NULL;
+	    }
+	} else if (c == ' ' || c == '\t' || c == '\v' || c == '\f' || c == '\r' || c == '\n') {
+	    continue;
+	} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || strchr("-./_:*+=", c)) {
+	    format = skip_sexp_format_token(format);
+	} else if (c == '%') {
+	    SV *sv;
+	    void **m = NULL;
+	    c = *format++;
+	    if (!nargs) {
+		format = 0;
+		break;
+	    }
+	    sv = *args++;
+	    nargs--;
+	    if (c == 'm' || c == 'M') {
+		Crypt_GCrypt_MPI gcma = dereference_gcm(sv);
+		m = malloc(sizeof(Crypt_GCrypt_MPI));
+		*m = gcma;
+	    } else if (c == 's') {
+		char *s = SvPV_nolen(sv);
+		m = malloc(sizeof(char **));
+		*m = s;
+	    } else if (c == 'b') {
+		STRLEN slen;
+		char *s = SvPV(sv, slen);
+		m = malloc(sizeof(int *));
+		*(int *)m = (int)slen;
+		arr = realloc(arr, (narr + 1) * sizeof(void **));
+		arr[narr++] = m;
+		m = malloc(sizeof(char **));
+		*m = s;
+	    } else if (c == 'd') {
+		IV i = SvIV(sv);
+		m = malloc(sizeof(int *));
+		*(int *)m = (int)i;
+	    } else if (c == 'u') {
+		UV u = SvUV(sv);
+		m = malloc(sizeof(unsigned int *));
+		*(unsigned int *)m = (int)u;
+	    } else if (c == 'S') {
+		if (!sv_derived_from(sv, "Crypt::GCrypt::Sexp")) {
+		    format = 0;
+		} else {
+		    IV tmp = SvIV((SV*)SvRV(sv));
+		    m = malloc(sizeof(Crypt_GCrypt_Sexp *));
+		    *m = INT2PTR(Crypt_GCrypt_Sexp, tmp);
+		}
+	    } else {
+		format = 0;
+	    }
+	    if (m) {
+		arr = realloc(arr, (narr + 1) * sizeof(void **));
+		arr[narr++] = m;
+	    }
+	} else if (c == '(' || c == ')') {
+	    continue;
+	} else
+	    format = NULL;
+    }
+    arr = realloc(arr, (narr + 1) * sizeof(void **));
+    arr[narr++] = NULL;
+    if (!format || nargs) {
+	free_sexp_build_array(arr);
+	arr = NULL;
+    }
+    return arr;
+}
+
 
 MODULE = Crypt::GCrypt        PACKAGE = Crypt::GCrypt    PREFIX = cg_
 
@@ -761,6 +953,58 @@ cg_DESTROY(gcr)
         Safefree(gcr->buffer);
         Safefree(gcr);
 
+
+Crypt_GCrypt_Sexp
+cg_pk_genkey(Crypt_GCrypt_Sexp params)
+    CODE:
+        gcry_error_t err;
+	Crypt_GCrypt_Sexp sexp = NULL;
+	err = gcry_pk_genkey(&sexp, params);
+        if (err != 0) croak("pk_genkey: %s", gcry_strerror(err));
+	RETVAL = sexp;
+    OUTPUT:
+        RETVAL
+
+Crypt_GCrypt_Sexp
+cg_pk_sign(Crypt_GCrypt_Sexp data, Crypt_GCrypt_Sexp skey)
+    CODE:
+        gcry_error_t err;
+	Crypt_GCrypt_Sexp sexp = NULL;
+	err = gcry_pk_sign(&sexp, data, skey);
+        if (err != 0) croak("pk_sign: %s", gcry_strerror(err));
+	RETVAL = sexp;
+    OUTPUT:
+        RETVAL
+
+void
+cg_pk_verify(Crypt_GCrypt_Sexp sig, Crypt_GCrypt_Sexp data, Crypt_GCrypt_Sexp pkey)
+    PPCODE:
+        gcry_error_t err;
+	err = gcry_pk_verify(sig, data, pkey);
+        if (err != 0) croak("pk_verify: %s", gcry_strerror(err));
+
+Crypt_GCrypt_Sexp
+cg_pk_encrypt(Crypt_GCrypt_Sexp data, Crypt_GCrypt_Sexp skey)
+    CODE:
+        gcry_error_t err;
+	Crypt_GCrypt_Sexp sexp = NULL;
+	err = gcry_pk_encrypt(&sexp, data, skey);
+        if (err != 0) croak("pk_encrypt: %s", gcry_strerror(err));
+	RETVAL = sexp;
+    OUTPUT:
+        RETVAL
+
+Crypt_GCrypt_Sexp
+cg_pk_decrypt(Crypt_GCrypt_Sexp data, Crypt_GCrypt_Sexp skey)
+    CODE:
+        gcry_error_t err;
+	Crypt_GCrypt_Sexp sexp = NULL;
+	err = gcry_pk_decrypt(&sexp, data, skey);
+        if (err != 0) croak("pk_decrypt: %s", gcry_strerror(err));
+	RETVAL = sexp;
+    OUTPUT:
+        RETVAL
+
 MODULE = Crypt::GCrypt        PACKAGE = Crypt::GCrypt::MPI    PREFIX = cgm_
 
 BOOT:
@@ -1104,3 +1348,149 @@ cgm_DESTROY(gmpi)
     CODE:
         gcry_mpi_release(gmpi);
         gmpi = NULL;
+
+MODULE = Crypt::GCrypt        PACKAGE = Crypt::GCrypt::Sexp    PREFIX = cgs_
+
+BOOT:
+    { /* found this method of storing constants in
+         http://blogs.sun.com/akolb/entry/pitfals_of_the_perl_xs */
+    HV *stash;
+    
+    stash = gv_stashpv("Crypt::GCrypt::Sexp", TRUE);
+    newCONSTSUB(stash, "FMT_DEFAULT", newSViv(GCRYSEXP_FMT_DEFAULT));
+    newCONSTSUB(stash, "FMT_CANON", newSViv(GCRYSEXP_FMT_CANON));
+    newCONSTSUB(stash, "FMT_BASE64", newSViv(GCRYSEXP_FMT_BASE64));
+    newCONSTSUB(stash, "FMT_ADVANCED", newSViv(GCRYSEXP_FMT_ADVANCED));
+    }
+
+void
+cgs_DESTROY(Crypt_GCrypt_Sexp sexp)
+    CODE:
+        gcry_sexp_release(sexp);
+        sexp = NULL;
+	
+Crypt_GCrypt_Sexp
+cgs_new(...)
+    PROTOTYPE: @
+    CODE:
+	int i = 0;
+	SV* sv;
+	int autodetect = 0;
+        gcry_error_t err;
+	gcry_sexp_t sexp = NULL;
+        char *s;
+	STRLEN slen = 0;
+        RETVAL = NULL;
+        s = SvPV_nolen(ST(0));
+        if (strcmp(s, "Crypt::GCrypt::Sexp") == 0)
+	    i = 1;
+	if (items < i + 1 || items > i + 2) croak("Wrong number of arguments for Crypt::GCrypt::Sexp->new()");
+	sv = ST(i);
+	if (items == i + 2)
+	    autodetect = (int)SvIV(ST(i + 1));
+        s = SvPV(sv, slen);
+	err = gcry_sexp_new(&sexp, s, slen, autodetect);
+        if (err != 0) croak("Crypt::GCrypt::Sexp::new: %s", gcry_strerror(err));
+	RETVAL = sexp;
+    OUTPUT:
+        RETVAL
+
+Crypt_GCrypt_Sexp
+cgs_build(...)
+    PROTOTYPE: @
+    CODE:
+	int i = 0;
+        char *s, *format;
+        RETVAL = NULL;
+        gcry_error_t err;
+	gcry_sexp_t sexp = NULL;
+	void **array;
+        s = SvPV_nolen(ST(0));
+        if (strcmp(s, "Crypt::GCrypt::Sexp") == 0)
+	    i = 1;
+        format = SvPV_nolen(ST(i));
+	array = create_sexp_build_array(format, &ST(i + 1), items - (i + 1));
+	if (!array) croak("Crypt::GCrypt::Sexp::build: could not parse format");
+	err = gcry_sexp_build_array(&sexp, NULL, format, array);
+	free_sexp_build_array(array);
+        if (err != 0) croak("GCrypt::Sexp::build: %s", gcry_strerror(err));
+	if (!sexp) croak("Crypt::GCrypt::Sexp::build: gcry_sexp_build_array returned NULL");
+	RETVAL = sexp;
+    OUTPUT:
+        RETVAL
+
+SV *
+cgs_print(sexp, mode = GCRYSEXP_FMT_DEFAULT)
+    Crypt_GCrypt_Sexp sexp;
+    int mode;
+    CODE:
+	size_t len = 0;
+	char *buf = NULL;
+	RETVAL = NULL;
+	len = gcry_sexp_sprint(sexp, mode, buf, len);
+	if (!len) croak("Crypt::GCrypt::Sexp::print: not a valid sexp");
+	buf = calloc(len, 1);
+	if (!buf) croak("Crypt::GCrypt::Sexp::print: out of memory");
+	gcry_sexp_sprint(sexp, mode, buf, len);
+	RETVAL = newSVpv(buf, len - 1);
+	free(buf);
+    OUTPUT:
+        RETVAL
+	
+void
+cgs_dump(Crypt_GCrypt_Sexp sexp)
+    PPCODE:
+	gcry_sexp_dump(sexp);
+	
+Crypt_GCrypt_Sexp
+cgs_nth(Crypt_GCrypt_Sexp sexp, int number, char *token = NULL)
+    CODE:
+	RETVAL = NULL;
+	if (token)
+	    sexp = gcry_sexp_find_token (sexp, token, 0);
+	if (sexp)
+	    RETVAL = gcry_sexp_nth(sexp, number);
+	if (token && sexp)
+	    gcry_sexp_release(sexp);
+    OUTPUT:
+        RETVAL
+
+SV *
+cgs_nth_data(Crypt_GCrypt_Sexp sexp, int number, char *token = NULL)
+    CODE:
+	RETVAL = NULL;
+	void *data = NULL;
+	size_t datalen = 0;
+	if (token)
+	    sexp = gcry_sexp_find_token (sexp, token, 0);
+	if (sexp)
+	    data = gcry_sexp_nth_buffer(sexp, number, &datalen);
+	if (data) {
+	    RETVAL = newSVpv((char *)data, datalen);
+	    gcry_free(data);
+	}
+	if (token && sexp)
+	    gcry_sexp_release(sexp);
+    OUTPUT:
+        RETVAL
+
+Crypt_GCrypt_MPI
+cgs_nth_mpi(Crypt_GCrypt_Sexp sexp, int number, int mpifmt = GCRYMPI_FMT_STD, char *token = NULL)
+    CODE:
+	RETVAL = NULL;
+	if (token)
+	    sexp = gcry_sexp_find_token (sexp, token, 0);
+	if (sexp)
+	    RETVAL = gcry_sexp_nth_mpi(sexp, number, mpifmt);
+	if (token && sexp)
+	    gcry_sexp_release(sexp);
+    OUTPUT:
+        RETVAL
+
+Crypt_GCrypt_Sexp
+cgs_find_token(Crypt_GCrypt_Sexp sexp, char *token)
+    CODE:
+	RETVAL = gcry_sexp_find_token (sexp, token, 0);
+    OUTPUT:
+        RETVAL
+
--- ./MANIFEST.orig	2022-06-23 11:11:49.350168550 +0000
+++ ./MANIFEST	2022-06-23 11:12:35.070082306 +0000
@@ -3,6 +3,7 @@ GCrypt.xs
 inc/Devel/CheckLib.pm
 lib/Crypt/GCrypt.pm
 lib/Crypt/GCrypt/MPI.pm
+lib/Crypt/GCrypt/Sexp.pm
 Makefile.PL
 MANIFEST			This list of files
 MANIFEST.SKIP
@@ -21,4 +22,3 @@ t/09-clone-digest.t
 t/10-versions.t
 t/20-mpi.t
 typemap
-META.json                                Module JSON meta-data (added by MakeMaker)
--- ./lib/Crypt/GCrypt.pm.orig	2022-06-23 11:11:56.630154817 +0000
+++ ./lib/Crypt/GCrypt.pm	2022-06-23 11:12:46.086061532 +0000
@@ -406,6 +406,45 @@ Completes the digest and return the resu
 multiple times, and it will return the same information.  Once a
 digest object has been read(), it may not be written to.
 
+=head1 LOW LEVEL PUBLIC KEY CRYPTOGRAPHIC FUNCTIONS
+
+The following function take S-expression objects as arguments and
+return an S-expression as result except for pk_verify, which does
+not return anything. See the libgcrypt documentation about the
+S-expression formats for the arguments and the returned data.
+
+=head2 pk_genkey()
+
+    my $result = Crypt::GCrypt::pk_genkey($params)
+
+Generate a new public key and private key.
+
+=head2 pk_encrypt()
+
+    my $encdata = Crypt::GCrypt::pk_encrypy($data, $pkey)
+
+Encrypt $data with the public key $pkey.
+
+=head2 pk_decrypt()
+
+    my $data = Crypt::GCrypt::pk_decrypt($encdata, $skey)
+
+Decrypt $encdata with the private key $skey.
+
+=head2 pk_sign()
+
+    my $sig = Crypt::GCrypt::pk_sign($data, $skey)
+
+Create a signature for data using the private key $skey.
+
+=head2 pk_verify()
+
+    Crypt::GCrypt::pk_verify($sig, $data, $pkey)
+
+Check whether the signature $sig matches the $data providing
+the public key $pkey. This function will croak() if the
+signature is bad.
+
 =head1 THREAD SAFETY
 
 libgcrypt is initialized with support for Pthread, so this module should be 
@@ -416,6 +455,9 @@ thread safe.
 Crypt::GCrypt::MPI supports Multi-precision integers (bignum math)
 using libgcrypt as the backend implementation.
 
+Crypt::GCrypt::Sexp supports S-expressions needed for libgcrypt's
+public key cryptography.
+
 =head1 BUGS AND FEEDBACK
 
 There are no known bugs. You are very welcome to write mail to the author 
@@ -428,6 +470,8 @@ Alessandro Ranellucci E<lt>aar@cpan.orgE
 
 Daniel Kahn Gillmor (message digests) E<lt>dkg@fifthhorseman.netE<gt>
 
+Michael Schroeder (public key cryptography) E<lt>mls@suse.deE<gt>
+
 =head1 COPYRIGHT AND LICENSE
 
 Copyright (c) Alessandro Ranellucci.
--- ./lib/Crypt/GCrypt/Sexp.pm.orig	2022-06-23 11:12:08.618132204 +0000
+++ ./lib/Crypt/GCrypt/Sexp.pm	2022-06-23 11:12:57.662039696 +0000
@@ -0,0 +1,148 @@
+# ===========================================================================
+# Crypt::GCrypt:Sexp
+#
+# Perl interface to multi-precision integers from the GNU Cryptographic library
+#
+# Author: Michael Schroeder E<lt>mls@suse.deE<gt>,
+# Copyright © 2022.
+#
+# Use this software AT YOUR OWN RISK.
+# See below for documentation.
+#
+
+package Crypt::GCrypt::Sexp;
+
+use strict;
+use warnings;
+
+use Crypt::GCrypt;
+
+1;
+__END__
+
+=encoding utf8
+
+=head1 NAME
+
+Crypt::GCrypt::Sexp - Perl interface to S-expressions from the GNU Cryptographic library
+
+=head1 SYNOPSIS
+
+  use Crypt::GCrypt::Sexp;
+
+  my $sexp = Crypt::GCrypt::Sexp->new('(foo)');
+
+=head1 ABSTRACT
+
+Crypt::GCrypt::Sexp provides an object interface to S-expressions
+from the C libgcrypt library.
+
+=head1 BASIC OPERATIONS
+
+=head2 new($buffer)
+
+Create a new S-expression object from its external representation
+
+  my $sexp = Crypt::GCrypt::Sexp->new('(foo)');
+
+The argument is expected to be in canonized format. To autodetect
+the format, add 1 as second parameter.
+
+=head2 build($template, $arg1, ...)
+
+Build a S-expression from a template and arguments. See the
+libgcrypt documentation for the formatting characters supported
+in the template.
+
+  my $sexp = Crypt::GCrypt::Sexp->build('(p %M)', $mpi);
+
+=head2 find_token($token)
+
+Scan the S-expression for a a sublist with type token. Return
+a new S-expression consisting of the sublist or undef when
+not found.
+
+  my $subsexp = $sexp->find_token('p');
+
+=head2 nth_mpi($number, $mpifmt)
+
+Return the mpi at the position $number in the S-expression.
+
+  my $mpi = $sexp->nth_mpi(1);
+
+It is possible to specify a token string as additional parameter
+to select the nth mpi of the sublist matching the token:
+
+  my $mpi_p = $sexp->nth_mpi(1, Crypt::GCrypt::MPI::FMT_USG, 'p');
+
+=head2 nth_data($number)
+
+Return the data at the position $number in the S-expression.
+You can optionally specify a token like with nth_mpi.
+
+  my $value = $sexp->nth_data(1, 'value')
+
+=head2 nth($number)
+
+Return a new S-expression from the element at positon $numer.
+You can optionally specify a token like with nth_mpi.
+
+  my $subsexp = $sexp->nth(1);
+
+=head1 OUTPUT AND DEBUGGING
+
+=head2 dump()
+
+Send the Sexp to the libgcrypt debugging stream.
+
+=head2 print($format)
+
+Return a string with the data of this S-expression, in a given format.  See
+CONVERSION FORMATS for the available formats.
+
+=head1 CONVERSION FORMATS
+
+The available printing and scanning formats are all in the
+Crypt::GCrypt::Sexp namespace, and have the same meanings as in gcrypt.
+
+=head2 FMT_DEFAULT
+
+A convenient external S-expression representation
+
+=head2 FMT_CANON
+
+The canonical format of a S-expression
+
+=head2 FMT_BASE64
+
+Not currently supported in libgcrypt
+
+=head2 FMT_ADVANCED
+
+An advanced representation of the S-expression
+
+=head1 AUTHORS AND CONTRIBUTORS
+
+Michael Schroeder E<lt>mls@suse.deE<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright © Michael Schroeder
+Crypt::GCrypt::Sexp is free software, you may redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 DISCLAIMER
+
+This software is provided by the copyright holders and contributors ``as
+is'' and any express or implied warranties, including, but not limited to,
+the implied warranties of merchantability and fitness for a particular
+purpose are disclaimed. In no event shall the regents or contributors be
+liable for any direct, indirect, incidental, special, exemplary, or
+consequential damages (including, but not limited to, procurement of
+substitute goods or services; loss of use, data, or profits; or business
+interruption) however caused and on any theory of liability, whether in
+contract, strict liability, or tort (including negligence or otherwise)
+arising in any way out of the use of this software, even if advised of the
+possibility of such damage.
+
+=cut
--- ./t/30-sexp.t.orig	2022-06-23 11:12:14.678120777 +0000
+++ ./t/30-sexp.t	2022-06-23 11:13:11.062014427 +0000
@@ -0,0 +1,39 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl 40-pk.t'
+
+#########################
+
+use Test;
+BEGIN { plan tests => 8 }; # <--- number of tests
+use ExtUtils::testlib;
+use Crypt::GCrypt;
+use Crypt::GCrypt::MPI;
+use Crypt::GCrypt::Sexp;
+
+#########################
+
+my $sexp1 = Crypt::GCrypt::Sexp->new('(7:subject(3:ref5:alice6:mother))');
+ok(defined $sexp1);
+my $sexp1_c = $sexp1->print(Crypt::GCrypt::Sexp::FMT_CANON);
+ok($sexp1_c, '(7:subject(3:ref5:alice6:mother))');
+
+my $data = "foo\0bar";
+my $mpi = Crypt::GCrypt::MPI->new(-7);
+my $umpi = Crypt::GCrypt::MPI->new(36);
+my $sexp2 = Crypt::GCrypt::Sexp->build('(mpi %m umpi %M data %b int %d uint %u str %s sub %S)', $mpi, $umpi, $data, -12, 28, 'hello', $sexp1);
+my $sexp2_c = $sexp2->print(Crypt::GCrypt::Sexp::FMT_CANON);
+ok($sexp2_c, "(3:mpi1:\xF94:umpi1:\x244:data7:foo\0bar3:int3:-124:uint2:283:str5:hello3:sub(7:subject(3:ref5:alice6:mother)))");
+
+my $ddata = $sexp2->nth_data(5);
+ok($data, $ddata);
+my $sexp3 = $sexp2->nth(13);
+my $sexp3_c = $sexp3->print(Crypt::GCrypt::Sexp::FMT_CANON);
+ok($sexp1_c, $sexp3_c);
+my $dmpi = $sexp2->nth_mpi(1);
+ok($mpi->cmp($dmpi) == 0);
+my $sexp4 = $sexp2->find_token('subject');
+my $sexp4_c = $sexp4->print(Crypt::GCrypt::Sexp::FMT_CANON);
+ok($sexp1_c, $sexp4_c);
+my $m = $sexp1->nth_data(2, 'ref');
+ok($m, 'mother');
+
--- ./typemap.orig	2022-06-23 11:12:01.270146064 +0000
+++ ./typemap	2022-06-23 11:13:06.054023870 +0000
@@ -1,6 +1,7 @@
 TYPEMAP
 Crypt_GCrypt		T_PTROBJ_SPECIAL
 Crypt_GCrypt_MPI	T_PTROBJ_SPECIAL
+Crypt_GCrypt_Sexp	T_PTROBJ_SPECIAL
 
 INPUT
 T_PTROBJ_SPECIAL
openSUSE Build Service is sponsored by