aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS2
-rw-r--r--src/data-identify.c230
-rw-r--r--src/gpgme.h.in2
3 files changed, 233 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index 7b939e76..32f3c84d 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,8 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_STATUS_TOFU_STATS NEW.
GPGME_STATUS_TOFU_STATS_LONG NEW.
GPGME_STATUS_NOTATION_FLAGS NEW.
+ GPGME_DATA_TYPE_PGP_ENCRYPTED NEW.
+ GPGME_DATA_TYPE_PGP_SIGNATURE NEW.
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]
diff --git a/src/data-identify.c b/src/data-identify.c
index 96006335..f7107e00 100644
--- a/src/data-identify.c
+++ b/src/data-identify.c
@@ -29,10 +29,238 @@
#include "util.h"
#include "parsetlv.h"
+
/* The size of the sample data we take for detection. */
#define SAMPLE_SIZE 2048
+/* OpenPGP packet types. */
+enum
+ {
+ PKT_NONE = 0,
+ PKT_PUBKEY_ENC = 1, /* Public key encrypted packet. */
+ PKT_SIGNATURE = 2, /* Secret key encrypted packet. */
+ PKT_SYMKEY_ENC = 3, /* Session key packet. */
+ PKT_ONEPASS_SIG = 4, /* One pass sig packet. */
+ PKT_SECRET_KEY = 5, /* Secret key. */
+ PKT_PUBLIC_KEY = 6, /* Public key. */
+ PKT_SECRET_SUBKEY = 7, /* Secret subkey. */
+ PKT_COMPRESSED = 8, /* Compressed data packet. */
+ PKT_ENCRYPTED = 9, /* Conventional encrypted data. */
+ PKT_MARKER = 10, /* Marker packet. */
+ PKT_PLAINTEXT = 11, /* Literal data packet. */
+ PKT_RING_TRUST = 12, /* Keyring trust packet. */
+ PKT_USER_ID = 13, /* User id packet. */
+ PKT_PUBLIC_SUBKEY = 14, /* Public subkey. */
+ PKT_OLD_COMMENT = 16, /* Comment packet from an OpenPGP draft. */
+ PKT_ATTRIBUTE = 17, /* PGP's attribute packet. */
+ PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */
+ PKT_MDC = 19, /* Manipulation detection code packet. */
+ };
+
+
+static inline unsigned long
+buf32_to_ulong (const void *buffer)
+{
+ const unsigned char *p = buffer;
+
+ return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+}
+
+
+/* Parse the next openpgp packet. This function assumes a valid
+ * OpenPGP packet at the address pointed to by BUFPTR which has a
+ * maximum length as stored at BUFLEN. Return the header information
+ * of that packet and advance the pointer stored at BUFPTR to the next
+ * packet; also adjust the length stored at BUFLEN to match the
+ * remaining bytes. If there are no more packets, store NULL at
+ * BUFPTR. Return an non-zero error code on failure or the following
+ * data on success:
+ *
+ * R_PKTTYPE = The packet type.
+ * R_NTOTAL = The total number of bytes of this packet
+ *
+ * If GPG_ERR_TRUNCATED is returned, a packet type is anyway stored at
+ * R_PKTTYPE but R_NOTAL won't have a usable value,
+ */
+static gpg_error_t
+next_openpgp_packet (unsigned char const **bufptr, size_t *buflen,
+ int *r_pkttype, size_t *r_ntotal)
+{
+ const unsigned char *buf = *bufptr;
+ size_t len = *buflen;
+ int c, ctb, pkttype;
+ unsigned long pktlen;
+
+ if (!len)
+ return gpg_error (GPG_ERR_NO_DATA);
+
+ ctb = *buf++; len--;
+ if ( !(ctb & 0x80) )
+ return gpg_error (GPG_ERR_INV_PACKET); /* Invalid CTB. */
+
+ if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */
+ {
+ pkttype = (ctb & 0x3f);
+ if (!len)
+ return gpg_error (GPG_ERR_INV_PACKET); /* No 1st length byte. */
+ c = *buf++; len--;
+ if ( c < 192 )
+ pktlen = c;
+ else if ( c < 224 )
+ {
+ pktlen = (c - 192) * 256;
+ if (!len)
+ return gpg_error (GPG_ERR_INV_PACKET); /* No 2nd length byte. */
+ c = *buf++; len--;
+ pktlen += c + 192;
+ }
+ else if (c == 255)
+ {
+ if (len < 4)
+ return gpg_error (GPG_ERR_INV_PACKET); /* No length bytes. */
+ pktlen = buf32_to_ulong (buf);
+ buf += 4;
+ len -= 4;
+ }
+ else /* Partial length encoding is not allowed for key packets. */
+ return gpg_error (GPG_ERR_UNEXPECTED);
+ }
+ else /* Old style CTB. */
+ {
+ int lenbytes;
+
+ pktlen = 0;
+ pkttype = (ctb>>2)&0xf;
+ lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
+ if (!lenbytes) /* Not allowed in key packets. */
+ return gpg_error (GPG_ERR_UNEXPECTED);
+ if (len < lenbytes)
+ return gpg_error (GPG_ERR_INV_PACKET); /* Not enough length bytes. */
+ for (; lenbytes; lenbytes--)
+ {
+ pktlen <<= 8;
+ pktlen |= *buf++; len--;
+ }
+ }
+
+ /* Do some basic sanity check. */
+ switch (pkttype)
+ {
+ case PKT_PUBKEY_ENC:
+ case PKT_SIGNATURE:
+ case PKT_SYMKEY_ENC:
+ case PKT_ONEPASS_SIG:
+ case PKT_SECRET_KEY:
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_SUBKEY:
+ case PKT_COMPRESSED:
+ case PKT_ENCRYPTED:
+ case PKT_MARKER:
+ case PKT_PLAINTEXT:
+ case PKT_RING_TRUST:
+ case PKT_USER_ID:
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_OLD_COMMENT:
+ case PKT_ATTRIBUTE:
+ case PKT_ENCRYPTED_MDC:
+ case PKT_MDC:
+ break; /* Okay these are allowed packets. */
+ default:
+ return gpg_error (GPG_ERR_UNEXPECTED);
+ }
+
+ if (pktlen > len)
+ {
+ /* Packet length header too long. This is possible because we
+ * may have only a truncated image. */
+ *r_pkttype = pkttype;
+ *r_ntotal = 0;
+ *bufptr = NULL;
+ return gpg_error (GPG_ERR_TRUNCATED);
+ }
+
+ *r_pkttype = pkttype;
+ *r_ntotal = (buf - *bufptr) + pktlen;
+
+ *bufptr = buf + pktlen;
+ *buflen = len - pktlen;
+ if (!*buflen)
+ *bufptr = NULL;
+
+ return 0;
+}
+
+
+/* Detection of PGP binary data. This function parses an OpenPGP
+ * message. This parser is robust enough to work on a truncated
+ * version. Returns a GPGME_DATA_TYPE_. */
+static gpgme_data_type_t
+pgp_binary_detection (const void *image_arg, size_t imagelen)
+{
+ gpg_error_t err = 0;
+ const unsigned char *image = image_arg;
+ size_t n;
+ int pkttype;
+ int anypacket = 0;
+ int allsignatures = 0;
+
+ while (!err && image)
+ {
+ err = next_openpgp_packet (&image, &imagelen, &pkttype, &n);
+ if (gpg_err_code (err) == GPG_ERR_TRUNCATED)
+ ;
+ else if (err)
+ break;
+
+ if (pkttype == PKT_SIGNATURE)
+ {
+ if (!anypacket)
+ allsignatures = 1;
+ }
+ else
+ allsignatures = 0;
+ anypacket = 1;
+
+ switch (pkttype)
+ {
+ case PKT_SIGNATURE:
+ break; /* We decide later. */
+
+ case PKT_PLAINTEXT:
+ /* Old style signature format: {sig}+,plaintext */
+ if (allsignatures)
+ return GPGME_DATA_TYPE_PGP_SIGNED;
+ break;
+
+ case PKT_ONEPASS_SIG:
+ return GPGME_DATA_TYPE_PGP_SIGNED;
+
+ case PKT_SECRET_KEY:
+ case PKT_PUBLIC_KEY:
+ return GPGME_DATA_TYPE_PGP_KEY;
+
+ case PKT_SECRET_SUBKEY:
+ case PKT_PUBLIC_SUBKEY:
+ return GPGME_DATA_TYPE_PGP_OTHER;
+ case PKT_PUBKEY_ENC:
+ case PKT_SYMKEY_ENC:
+ return GPGME_DATA_TYPE_PGP_ENCRYPTED;
+
+ case PKT_MARKER:
+ break; /* Skip this packet. */
+
+ default:
+ return GPGME_DATA_TYPE_PGP_OTHER;
+ }
+ }
+
+ if (allsignatures)
+ return GPGME_DATA_TYPE_PGP_SIGNATURE;
+
+ return GPGME_DATA_TYPE_UNKNOWN;
+}
+
/* Note that DATA may be binary but a final nul is required so that
string operations will find a terminator.
@@ -167,7 +395,7 @@ basic_detection (const char *data, size_t datalen)
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;
+ return pgp_binary_detection (data, datalen);
}
/* Now check whether there are armor lines. */
diff --git a/src/gpgme.h.in b/src/gpgme.h.in
index dc2f1433..790485d5 100644
--- a/src/gpgme.h.in
+++ b/src/gpgme.h.in
@@ -239,8 +239,10 @@ typedef enum
GPGME_DATA_TYPE_INVALID = 0, /* Not detected. */
GPGME_DATA_TYPE_UNKNOWN = 1,
GPGME_DATA_TYPE_PGP_SIGNED = 0x10,
+ GPGME_DATA_TYPE_PGP_ENCRYPTED= 0x11,
GPGME_DATA_TYPE_PGP_OTHER = 0x12,
GPGME_DATA_TYPE_PGP_KEY = 0x13,
+ GPGME_DATA_TYPE_PGP_SIGNATURE= 0x18, /* Detached signature */
GPGME_DATA_TYPE_CMS_SIGNED = 0x20,
GPGME_DATA_TYPE_CMS_ENCRYPTED= 0x21,
GPGME_DATA_TYPE_CMS_OTHER = 0x22,