diff --git a/NEWS b/NEWS
index 5c871dd8..8f700fde 100644
--- a/NEWS
+++ b/NEWS
@@ -7,9 +7,13 @@ Noteworthy changes in version 1.4.3 (unreleased)
* Under Windows the default engines names are first searched in the
installation directory of the gpgme DLL.
+ * New function gpgme_data_identify to detect the type of a message.
+
* Interface changes relative to the 1.4.2 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
gpgme_signers_count NEW.
+ gpgme_data_type_t NEW.
+ gpgme_data_identify NEW.
Noteworthy changes in version 1.4.2 (2013-05-28)
diff --git a/doc/gpgme.texi b/doc/gpgme.texi
index 08e2f0f0..4ec0bfe2 100644
--- a/doc/gpgme.texi
+++ b/doc/gpgme.texi
@@ -1885,6 +1885,7 @@ be used to manipulate both.
@menu
* Data Buffer I/O Operations:: I/O operations on data buffers.
* Data Buffer Meta-Data:: Meta-data manipulation of data buffers.
+* Data Buffer Convenience:: Convenience fucntion for data buffers.
@end menu
@@ -2047,6 +2048,56 @@ The function @code{gpgme_data_set_encoding} changes the encoding of
the data object with the handle @var{dh} to @var{enc}.
@end deftypefun
+@node Data Buffer Convenience
+@subsection Data Buffer Convenience Functions
+@cindex data buffer, convenience
+@cindex type of data
+@cindex identify
+
+@deftp {Data type} {enum gpgme_data_type_t}
+@tindex gpgme_data_type_t
+The @code{gpgme_data_type_t} type is used to return the detected type
+of the content of a data buffer.
+@end deftp
+
+@table @code
+@item GPGME_DATA_TYPE_INVALID
+This is returned by @code{gpgme_data_identify} if it was not possible
+to identify the data. Reasons for this might be a non-seekable stream
+or a memory problem. The value is 0.
+@item GPGME_DATA_TYPE_UNKNOWN
+The type of the data is not known.
+@item GPGME_DATA_TYPE_PGP_SIGNED
+The data is an OpenPGP signed message. This may be a binary
+signature, a detached one or a cleartext signature.
+@item GPGME_DATA_TYPE_PGP_OTHER
+This is a generic OpenPGP message. In most cases this will be
+encrypted data.
+@item GPGME_DATA_TYPE_PGP_KEY
+This is an OpenPGP key (private or public).
+@item GPGME_DATA_TYPE_CMS_SIGNED
+This is a CMS signed message.
+@item GPGME_DATA_TYPE_CMS_ENCRYPTED
+This is a CMS encrypted (enveloped data) message.
+@item GPGME_DATA_TYPE_CMS_OTHER
+This is used for other CMS message types.
+@item GPGME_DATA_TYPE_X509_CERT
+The data is a X.509 certificate
+@item GPGME_DATA_TYPE_PKCS12
+The data is a PKCS#12 message. This is commonly used to exchange
+private keys for X.509.
+@end table
+
+@deftypefun gpgme_data_type_t gpgme_data_identify (@w{gpgme_data_t @var{dh}})
+The function @code{gpgme_data_identify} returns the type of the data
+with the handle @var{dh}. If it is not possible to perform the
+identification, the function returns zero
+(@code{GPGME_DATA_TYPE_INVALID}). Note that depending on how the data
+object has been created the identification may not be possible or the
+data object may change its internal state (file pointer moved). For
+file or memory based data object, the state should not change.
+@end deftypefun
+
@c
@c Chapter Contexts
diff --git a/src/Makefile.am b/src/Makefile.am
index 37e34071..1f951039 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -103,8 +103,9 @@ endif
# unresolved symbols to the thread module.
main_sources = \
util.h conversion.c get-env.c context.h ops.h \
+ parsetlv.c parsetlv.h \
data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \
- data-compat.c \
+ data-compat.c data-identify.c \
signers.c sig-notation.c \
wait.c wait-global.c wait-private.c wait-user.c wait.h \
op-support.c \
diff --git a/src/data-identify.c b/src/data-identify.c
new file mode 100644
index 00000000..96006335
--- /dev/null
+++ b/src/data-identify.c
@@ -0,0 +1,247 @@
+/* data-identify.c - Try to identify the data
+ Copyright (C) 2013 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 Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 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 .
+ */
+
+#if HAVE_CONFIG_H
+# include
+#endif
+
+#include
+#include
+
+#include "gpgme.h"
+#include "data.h"
+#include "util.h"
+#include "parsetlv.h"
+
+/* The size of the sample data we take for detection. */
+#define SAMPLE_SIZE 2048
+
+
+
+/* Note that DATA may be binary but a final nul is required so that
+ string operations will find a terminator.
+
+ Returns: GPGME_DATA_TYPE_xxxx */
+static gpgme_data_type_t
+basic_detection (const char *data, size_t datalen)
+{
+ tlvinfo_t ti;
+ const char *s;
+ size_t n;
+ int maybe_p12 = 0;
+
+ if (datalen < 24) /* Object is probably too short for detection. */
+ return GPGME_DATA_TYPE_UNKNOWN;
+
+ /* This is a common example of a CMS object - it is obvious that we
+ only need to read a few bytes to get to the OID:
+ 30 82 0B 59 06 09 2A 86 48 86 F7 0D 01 07 02 A0 82 0B 4A 30 82 0B 46 02
+ ----------- ++++++++++++++++++++++++++++++++
+ SEQUENCE OID (signedData)
+ (2 byte len)
+
+ A PKCS#12 message is:
+
+ 30 82 08 59 02 01 03 30 82 08 1F 06 09 2A 86 48 86 F7 0D 01 07 01 A0 82
+ ----------- ++++++++ ----------- ++++++++++++++++++++++++++++++++
+ SEQUENCE INTEGER SEQUENCE OID (data)
+
+ A X.509 certificate is:
+
+ 30 82 05 B8 30 82 04 A0 A0 03 02 01 02 02 07 15 46 A0 BF 30 07 39 30 0D
+ ----------- +++++++++++ ----- ++++++++ --------------------------
+ SEQUENCE SEQUENCE [0] INTEGER INTEGER SEQU
+ (tbs) (version) (s/n) (Algo)
+
+ Thus we need to read at least 22 bytes, we add 2 bytes to cope with
+ length headers stored with 4 bytes.
+ */
+
+
+ s = data;
+ n = datalen;
+
+ if (parse_tlv (&s, &n, &ti))
+ goto try_pgp; /* Not properly BER encoded. */
+ if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
+ && ti.is_cons))
+ goto try_pgp; /* A CMS object always starts with a sequence. */
+
+ if (parse_tlv (&s, &n, &ti))
+ goto try_pgp; /* Not properly BER encoded. */
+ if (ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
+ && ti.is_cons && n >= ti.length)
+ {
+ if (parse_tlv (&s, &n, &ti))
+ goto try_pgp;
+ if (!(ti.cls == ASN1_CLASS_CONTEXT && ti.tag == 0
+ && ti.is_cons && ti.length == 3 && n >= ti.length))
+ goto try_pgp;
+
+ if (parse_tlv (&s, &n, &ti))
+ goto try_pgp;
+ if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_INTEGER
+ && !ti.is_cons && ti.length == 1 && n && (*s == 1 || *s == 2)))
+ goto try_pgp;
+ s++;
+ n--;
+ if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_INTEGER
+ && !ti.is_cons))
+ goto try_pgp;
+ /* Because the now following S/N may be larger than the sample
+ data we have, we stop parsing here and don't check for the
+ algorithm ID. */
+ return GPGME_DATA_TYPE_X509_CERT;
+ }
+ if (ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_INTEGER
+ && !ti.is_cons && ti.length == 1 && n && *s == 3)
+ {
+ maybe_p12 = 1;
+ s++;
+ n--;
+ if (parse_tlv (&s, &n, &ti))
+ goto try_pgp;
+ if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
+ && ti.is_cons))
+ goto try_pgp;
+ if (parse_tlv (&s, &n, &ti))
+ goto try_pgp;
+ }
+ if (ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_OBJECT_ID
+ && !ti.is_cons && ti.length && n >= ti.length)
+ {
+ if (ti.length == 9)
+ {
+ if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x01", 9))
+ {
+ /* Data. */
+ return (maybe_p12 ? GPGME_DATA_TYPE_PKCS12
+ /* */ : GPGME_DATA_TYPE_CMS_OTHER);
+ }
+ if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x02", 9))
+ {
+ /* Signed Data. */
+ return (maybe_p12 ? GPGME_DATA_TYPE_PKCS12
+ /* */ : GPGME_DATA_TYPE_CMS_SIGNED);
+ }
+ if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x03", 9))
+ return GPGME_DATA_TYPE_CMS_ENCRYPTED; /* Enveloped Data. */
+ if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x05", 9))
+ return GPGME_DATA_TYPE_CMS_OTHER; /* Digested Data. */
+ if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x06", 9))
+ return GPGME_DATA_TYPE_CMS_OTHER; /* Encrypted Data. */
+ }
+ else if (ti.length == 11)
+ {
+ if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x01\x02", 11))
+ return GPGME_DATA_TYPE_CMS_OTHER; /* Auth Data. */
+ }
+ }
+
+
+ try_pgp:
+ /* Check whether this might be a non-armored PGP message. We need
+ to do this before checking for armor lines, so that we don't get
+ fooled by armored messages inside a signed binary PGP message. */
+ if ((data[0] & 0x80))
+ {
+ /* That might be a binary PGP message. At least it is not plain
+ ASCII. Of course this might be certain lead-in text of
+ armored CMS messages. However, I am not sure whether this is
+ at all defined and in any case it is uncommon. Thus we don't
+ do any further plausibility checks but stupidly assume no CMS
+ armored data will follow. */
+ return GPGME_DATA_TYPE_UNKNOWN;
+ }
+
+ /* Now check whether there are armor lines. */
+ for (s = data; s && *s; s = (*s=='\n')?(s+1):((s=strchr (s,'\n'))?(s+1):s))
+ {
+ if (!strncmp (s, "-----BEGIN ", 11))
+ {
+ if (!strncmp (s+11, "SIGNED ", 7))
+ return GPGME_DATA_TYPE_CMS_SIGNED;
+ if (!strncmp (s+11, "ENCRYPTED ", 10))
+ return GPGME_DATA_TYPE_CMS_ENCRYPTED;
+ if (!strncmp (s+11, "PGP ", 4))
+ {
+ if (!strncmp (s+15, "SIGNATURE", 9))
+ return GPGME_DATA_TYPE_PGP_SIGNED;
+ if (!strncmp (s+15, "SIGNED MESSAGE", 14))
+ return GPGME_DATA_TYPE_PGP_SIGNED;
+ if (!strncmp (s+15, "PUBLIC KEY BLOCK", 16))
+ return GPGME_DATA_TYPE_PGP_KEY;
+ if (!strncmp (s+15, "PRIVATE KEY BLOCK", 17))
+ return GPGME_DATA_TYPE_PGP_KEY;
+ if (!strncmp (s+15, "SECRET KEY BLOCK", 16))
+ return GPGME_DATA_TYPE_PGP_KEY;
+ if (!strncmp (s+15, "ARMORED FILE", 12))
+ return GPGME_DATA_TYPE_UNKNOWN;
+ return GPGME_DATA_TYPE_PGP_OTHER; /* PGP MESSAGE */
+ }
+ if (!strncmp (s+11, "CERTIFICATE", 11))
+ return GPGME_DATA_TYPE_X509_CERT;
+ if (!strncmp (s+11, "PKCS12", 6))
+ return GPGME_DATA_TYPE_PKCS12;
+ return GPGME_DATA_TYPE_CMS_OTHER; /* Not PGP, thus we assume CMS. */
+ }
+ }
+
+ return GPGME_DATA_TYPE_UNKNOWN;
+}
+
+
+/* Try to detect the type of the data. Note that this function works
+ only on seekable data objects. The function tries to reset the
+ file pointer but there is no guarantee that it will work.
+
+ FIXME: We may want to add internal buffering so that this function
+ can be implemented for allmost all kind of data objects.
+ */
+gpgme_data_type_t
+gpgme_data_identify (gpgme_data_t dh, int reserved)
+{
+ gpgme_data_type_t result;
+ char *sample;
+ int n;
+ gpgme_off_t off;
+
+ /* Check whether we can seek the data object. */
+ off = gpgme_data_seek (dh, 0, SEEK_CUR);
+ if (off == (gpgme_off_t)(-1))
+ return GPGME_DATA_TYPE_INVALID;
+
+ /* Allocate a buffer and read the data. */
+ sample = malloc (SAMPLE_SIZE);
+ if (!sample)
+ return GPGME_DATA_TYPE_INVALID; /* Ooops. */
+ n = gpgme_data_read (dh, sample, SAMPLE_SIZE - 1);
+ if (n < 0)
+ {
+ free (sample);
+ return GPGME_DATA_TYPE_INVALID; /* Ooops. */
+ }
+ sample[n] = 0; /* (Required for our string functions.) */
+
+ result = basic_detection (sample, n);
+ free (sample);
+ gpgme_data_seek (dh, off, SEEK_SET);
+
+ return result;
+}
diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c
index 0ebababb..2bf7654a 100644
--- a/src/gpgme-tool.c
+++ b/src/gpgme-tool.c
@@ -1435,7 +1435,8 @@ typedef enum status
STATUS_INCLUDE_CERTS,
STATUS_KEYLIST_MODE,
STATUS_RECIPIENT,
- STATUS_ENCRYPT_RESULT
+ STATUS_ENCRYPT_RESULT,
+ STATUS_IDENTIFY_RESULT
} status_t;
const char *status_string[] =
@@ -1448,7 +1449,8 @@ const char *status_string[] =
"INCLUDE_CERTS",
"KEYLIST_MODE",
"RECIPIENT",
- "ENCRYPT_RESULT"
+ "ENCRYPT_RESULT",
+ "IDENTIFY_RESULT"
};
struct gpgme_tool
@@ -2065,11 +2067,6 @@ gt_vfs_create (gpgme_tool_t gt, const char *container_file, int flags)
}
-static const char hlp_passwd[] =
- "PASSWD \n"
- "\n"
- "Ask the backend to change the passphrase for the key\n"
- "specified by USER-ID.";
gpg_error_t
gt_passwd (gpgme_tool_t gt, char *fpr)
{
@@ -2086,6 +2083,29 @@ gt_passwd (gpgme_tool_t gt, char *fpr)
}
+gpg_error_t
+gt_identify (gpgme_tool_t gt, gpgme_data_t data)
+{
+ const char *s = "?";
+
+ switch (gpgme_data_identify (data, 0))
+ {
+ case GPGME_DATA_TYPE_INVALID: return gpg_error (GPG_ERR_GENERAL);
+ case GPGME_DATA_TYPE_UNKNOWN : s = "unknown"; break;
+ case GPGME_DATA_TYPE_PGP_SIGNED : s = "PGP-signed"; break;
+ case GPGME_DATA_TYPE_PGP_OTHER : s = "PGP"; break;
+ case GPGME_DATA_TYPE_PGP_KEY : s = "PGP-key"; break;
+ case GPGME_DATA_TYPE_CMS_SIGNED : s = "CMS-signed"; break;
+ case GPGME_DATA_TYPE_CMS_ENCRYPTED: s = "CMS-encrypted"; break;
+ case GPGME_DATA_TYPE_CMS_OTHER : s = "CMS"; break;
+ case GPGME_DATA_TYPE_X509_CERT : s = "X.509"; break;
+ case GPGME_DATA_TYPE_PKCS12 : s = "PKCS12"; break;
+ }
+ gt_write_status (gt, STATUS_IDENTIFY_RESULT, s, NULL);
+ return 0;
+}
+
+
#define GT_RESULT_ENCRYPT 0x1
#define GT_RESULT_DECRYPT 0x2
#define GT_RESULT_SIGN 0x4
@@ -3374,6 +3394,11 @@ cmd_vfs_create (assuan_context_t ctx, char *line)
}
+static const char hlp_passwd[] =
+ "PASSWD \n"
+ "\n"
+ "Ask the backend to change the passphrase for the key\n"
+ "specified by USER-ID.";
static gpg_error_t
cmd_passwd (assuan_context_t ctx, char *line)
{
@@ -3430,6 +3455,39 @@ cmd_hash_algo_name (assuan_context_t ctx, char *line)
}
+static const char hlp_identify[] =
+ "IDENTIY\n"
+ "\n"
+ "Identify the type of data set with the INPUT command.";
+static gpg_error_t
+cmd_identify (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpg_error_t err;
+ assuan_fd_t inp_fd;
+ char *inp_fn;
+ gpgme_data_t inp_data;
+
+ inp_fd = server->input_fd;
+ inp_fn = server->input_filename;
+ if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
+ return GPG_ERR_ASS_NO_INPUT;
+
+ err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
+ &server->input_stream);
+ if (err)
+ return err;
+
+ err = gt_identify (server->gt, inp_data);
+
+ gpgme_data_release (inp_data);
+ server_reset_fds (server);
+
+ return err;
+}
+
+
+
/* Tell the assuan library about our commands. */
static gpg_error_t
register_commands (assuan_context_t ctx)
@@ -3488,6 +3546,7 @@ register_commands (assuan_context_t ctx)
{ "PUBKEY_ALGO_NAME", cmd_pubkey_algo_name },
{ "HASH_ALGO_NAME", cmd_hash_algo_name },
{ "PASSWD", cmd_passwd, hlp_passwd },
+ { "IDENTIFY", cmd_identify, hlp_identify },
{ NULL }
};
int idx;
diff --git a/src/gpgme.def b/src/gpgme.def
index 7610d37e..0478cb62 100644
--- a/src/gpgme.def
+++ b/src/gpgme.def
@@ -211,5 +211,7 @@ EXPORTS
gpgme_signers_count @160
+ gpgme_data_identify @161
+
; END
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index f644a509..5c4de6be 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -210,6 +210,22 @@ typedef enum
}
gpgme_data_encoding_t;
+/* Known data types. */
+typedef enum
+ {
+ GPGME_DATA_TYPE_INVALID = 0, /* Not detected. */
+ GPGME_DATA_TYPE_UNKNOWN = 1,
+ GPGME_DATA_TYPE_PGP_SIGNED = 0x10,
+ GPGME_DATA_TYPE_PGP_OTHER = 0x12,
+ GPGME_DATA_TYPE_PGP_KEY = 0x13,
+ GPGME_DATA_TYPE_CMS_SIGNED = 0x20,
+ GPGME_DATA_TYPE_CMS_ENCRYPTED= 0x21,
+ GPGME_DATA_TYPE_CMS_OTHER = 0x22,
+ GPGME_DATA_TYPE_X509_CERT = 0x23,
+ GPGME_DATA_TYPE_PKCS12 = 0x24,
+ }
+gpgme_data_type_t;
+
/* Public key algorithms from libgcrypt. */
typedef enum
@@ -1149,6 +1165,9 @@ char *gpgme_data_get_file_name (gpgme_data_t dh);
gpgme_error_t gpgme_data_set_file_name (gpgme_data_t dh,
const char *file_name);
+/* Try to identify the type of the data in DH. */
+gpgme_data_type_t gpgme_data_identify (gpgme_data_t dh, int reserved);
+
/* Create a new data buffer which retrieves the data from the callback
function READ_CB. Deprecated, please use gpgme_data_new_from_cbs
diff --git a/src/libgpgme.vers b/src/libgpgme.vers
index 0b2e89db..fe18e6a5 100644
--- a/src/libgpgme.vers
+++ b/src/libgpgme.vers
@@ -29,6 +29,7 @@ GPGME_1.1 {
gpgme_data_set_file_name;
gpgme_data_get_file_name;
+ gpgme_data_identify;
gpgme_sig_notation_clear;
gpgme_sig_notation_add;
diff --git a/src/parsetlv.c b/src/parsetlv.c
new file mode 100644
index 00000000..70c95189
--- /dev/null
+++ b/src/parsetlv.c
@@ -0,0 +1,103 @@
+/* parsetlv.c - ASN.1 TLV functions
+ * Copyright (C) 2005, 2007, 2008, 2012 g10 Code GmbH
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 .
+ */
+
+#ifdef HAVE_CONFIG_H
+# include
+#endif
+#include
+#include
+#include
+
+#include "parsetlv.h"
+
+
+/* Simple but pretty complete ASN.1 BER parser. Parse the data at the
+ address of BUFFER with a length given at the address of SIZE. On
+ success return 0 and update BUFFER and SIZE to point to the value.
+ Do not update them on error. The information about the object are
+ stored in the caller allocated TI structure. */
+int
+_gpgme_parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti)
+{
+ int c;
+ unsigned long tag;
+ const unsigned char *buf = (const unsigned char *)(*buffer);
+ size_t length = *size;
+
+ ti->cls = 0;
+ ti->tag = 0;
+ ti->is_cons = 0;
+ ti->is_ndef = 0;
+ ti->length = 0;
+ ti->nhdr = 0;
+
+ if (!length)
+ return -1;
+ c = *buf++; length--; ++ti->nhdr;
+
+ ti->cls = (c & 0xc0) >> 6;
+ ti->is_cons = !!(c & 0x20);
+ tag = c & 0x1f;
+
+ if (tag == 0x1f)
+ {
+ tag = 0;
+ do
+ {
+ tag <<= 7;
+ if (!length)
+ return -1;
+ c = *buf++; length--; ++ti->nhdr;
+ tag |= c & 0x7f;
+ }
+ while (c & 0x80);
+ }
+ ti->tag = tag;
+
+ if (!length)
+ return -1;
+ c = *buf++; length--; ++ti->nhdr;
+
+ if ( !(c & 0x80) )
+ ti->length = c;
+ else if (c == 0x80)
+ ti->is_ndef = 1;
+ else if (c == 0xff)
+ return -1;
+ else
+ {
+ unsigned long len = 0;
+ int count = (c & 0x7f);
+
+ if (count > sizeof (len) || count > sizeof (size_t))
+ return -1;
+
+ for (; count; count--)
+ {
+ len <<= 8;
+ if (!length)
+ return -1;
+ c = *buf++; length--; ++ti->nhdr;
+ len |= c & 0xff;
+ }
+ ti->length = len;
+ }
+
+ *buffer = (void*)buf;
+ *size = length;
+ return 0;
+}
diff --git a/src/parsetlv.h b/src/parsetlv.h
new file mode 100644
index 00000000..153073c1
--- /dev/null
+++ b/src/parsetlv.h
@@ -0,0 +1,48 @@
+/* parsetlv.h - TLV functions defintions
+ * Copyright (C) 2012 g10 Code GmbH
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 .
+ */
+
+#ifndef PARSETLV_H
+#define PARSETLV_H
+
+/* ASN.1 constants. */
+#define ASN1_CLASS_UNIVERSAL 0
+#define ASN1_CLASS_APPLICATION 1
+#define ASN1_CLASS_CONTEXT 2
+#define ASN1_CLASS_PRIVATE 3
+#define ASN1_TAG_INTEGER 2
+#define ASN1_TAG_OBJECT_ID 6
+#define ASN1_TAG_SEQUENCE 16
+
+
+/* Object used with parse_tlv. */
+struct tlvinfo_s
+{
+ int cls; /* The class of the tag. */
+ int tag; /* The tag. */
+ int is_cons; /* True if it is a constructed object. */
+ int is_ndef; /* True if the object has an indefinite length. */
+ size_t length; /* The length of the value. */
+ size_t nhdr; /* The number of octets in the header (tag,length). */
+};
+typedef struct tlvinfo_s tlvinfo_t;
+
+/*-- parsetlv.c --*/
+int _gpgme_parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti);
+#define parse_tlv(a,b,c) _gpgme_parse_tlv ((a), (b), (c))
+
+
+#endif /*PARSETLV_H*/