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