[svn] GPGol - r170 - trunk/src
svn author wk
cvs at cvs.gnupg.org
Fri Aug 10 09:17:58 CEST 2007
Author: wk
Date: 2007-08-10 09:17:28 +0200 (Fri, 10 Aug 2007)
New Revision: 170
Added:
trunk/src/item-events.cpp
trunk/src/item-events.h
trunk/src/message.cpp
trunk/src/message.h
trunk/src/mimeparser.c
trunk/src/mimeparser.h
trunk/src/serpent.c
trunk/src/serpent.h
Log:
Add missing files
Added: trunk/src/item-events.cpp
===================================================================
--- trunk/src/item-events.cpp 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/item-events.cpp 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,131 @@
+/* item-events.cpp - GpgolItemEvents implementation
+ * Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <windows.h>
+
+#include "mymapi.h"
+#include "mymapitags.h"
+#include "myexchext.h"
+#include "common.h"
+#include "olflange-def.h"
+#include "olflange.h"
+#include "item-events.h"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+ SRCNAME, __func__, __LINE__); \
+ } while (0)
+
+
+
+/* Our constructor. */
+GpgolItemEvents::GpgolItemEvents (GpgolExt *pParentInterface)
+{
+ m_pExchExt = pParentInterface;
+ m_ref = 0;
+}
+
+
+/* The QueryInterfac. */
+STDMETHODIMP
+GpgolItemEvents::QueryInterface (REFIID riid, LPVOID FAR *ppvObj)
+{
+ *ppvObj = NULL;
+ if (riid == IID_IOutlookExtItemEvents)
+ {
+ *ppvObj = (LPVOID)this;
+ AddRef ();
+ return S_OK;
+ }
+ if (riid == IID_IUnknown)
+ {
+ *ppvObj = (LPVOID)m_pExchExt;
+ m_pExchExt->AddRef ();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+
+/* This method is called if an item is about to being displayed.
+ Possible return values are:
+
+ S_FALSE - Let Outlook continue the operation.
+
+ E_ABORT - Abort the open operation and the item is not being
+ displayed.
+*/
+STDMETHODIMP
+GpgolItemEvents::OnOpen (LPEXCHEXTCALLBACK peecb)
+{
+ log_debug ("%s:%s: received", SRCNAME, __func__);
+ return S_FALSE;
+}
+
+
+/* Like all the other Complete methods this one is called after OnOpen
+ has been called for all registred extensions. FLAGS may have tehse
+ values:
+
+ 0 - The open action has not been canceled.
+
+ EEME_FAILED or EEME_COMPLETE_FAILED - both indicate that the
+ open action has been canceled.
+
+ Note that this method may be called more than once for each OnOpen
+ and may even occur without an OnOpen (i.e. while setting up a new
+ extension).
+
+ The same return values as for OnOpen may be used..
+*/
+STDMETHODIMP
+GpgolItemEvents::OnOpenComplete (LPEXCHEXTCALLBACK peecb, ULONG flags)
+{
+ log_debug ("%s:%s: received, flags=%#lx", SRCNAME, __func__, flags);
+ return S_FALSE;
+}
+
+
+/* This method is called if an item's window received a close request.
+ Possible return values are:
+
+ S_FALSE - Let Outlook continue the operation.
+
+ E_ABORT - Abort the close operation and don't dismiss the window.
+*/
+STDMETHODIMP
+GpgolItemEvents::OnClose (LPEXCHEXTCALLBACK peecb, ULONG save_options)
+{
+ log_debug ("%s:%s: received, options=%#lx", SRCNAME, __func__, save_options);
+ return S_FALSE;
+}
+
+
+/* This is the corresponding Complete method for OnClose. See
+ OnOpenComplete for a description. */
+STDMETHODIMP
+GpgolItemEvents::OnCloseComplete (LPEXCHEXTCALLBACK peecb, ULONG flags)
+{
+ log_debug ("%s:%s: received, flags=%#lx", SRCNAME, __func__, flags);
+ return S_FALSE;
+}
Added: trunk/src/item-events.h
===================================================================
--- trunk/src/item-events.h 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/item-events.h 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,64 @@
+/* item-events.h - GpgolItemEvents definitions.
+ * Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+#ifndef ITEM_EVENTS_H
+#define ITEM_EVENTS_H
+
+/*
+ GpgolItemEvents
+
+ The GpgolItemEvents class implements the processing of events
+ related to certain items. This is currently open and close of an
+ inspector.
+ */
+class GpgolItemEvents : public IOutlookExtItemEvents
+{
+ public:
+ GpgolItemEvents (GpgolExt *pParentInterface);
+
+ private:
+ GpgolExt *m_pExchExt;
+ ULONG m_ref;
+
+ public:
+ STDMETHODIMP QueryInterface (REFIID riid, LPVOID FAR *ppvObj);
+ inline STDMETHODIMP_(ULONG) AddRef (void)
+ {
+ ++m_ref;
+ return m_ref;
+ }
+ inline STDMETHODIMP_(ULONG) Release (void)
+ {
+ ULONG count = --m_ref;
+ if (!count)
+ delete this;
+ return count;
+ }
+
+
+ STDMETHODIMP OnOpen (LPEXCHEXTCALLBACK peecb);
+ STDMETHODIMP OnOpenComplete (LPEXCHEXTCALLBACK peecb, ULONG flags);
+ STDMETHODIMP OnClose (LPEXCHEXTCALLBACK peecb, ULONG save_options);
+ STDMETHODIMP OnCloseComplete (LPEXCHEXTCALLBACK peecb, ULONG flags);
+};
+
+#endif /*ATTACHED_FILE_EVENTS_H*/
Added: trunk/src/message.cpp
===================================================================
--- trunk/src/message.cpp 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/message.cpp 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,268 @@
+/* message.cpp - Functions for message handling
+ * Copyright (C) 2006, 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <windows.h>
+
+#include "mymapi.h"
+#include "mymapitags.h"
+#include "myexchext.h"
+#include "common.h"
+#include "mapihelp.h"
+#include "mimeparser.h"
+#include "message.h"
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+ SRCNAME, __func__, __LINE__); \
+ } while (0)
+
+
+
+/* Verify MESSAGE and update the attachments as required. MSGTYPE
+ should be the type of the message so that the fucntion can decide
+ what to do. With FORCE set the verification is done regardlessless
+ of a cached signature result. */
+int
+message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
+{
+ mapi_attach_item_t *table;
+ int moss_idx = -1;
+ int i;
+ char *inbuf;
+ size_t inbuflen;
+ int err;
+
+ switch (msgtype)
+ {
+ case MSGTYPE_GPGOL_MULTIPART_SIGNED:
+ break;
+ case MSGTYPE_GPGOL_OPAQUE_SIGNED:
+ log_debug ("Opaque signed message are not yet supported!");
+ return 0;
+ case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+ case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+ return -1; /* Should not be called for such a message. */
+ case MSGTYPE_UNKNOWN:
+ case MSGTYPE_GPGOL:
+ return 0; /* Nothing to do. */
+ }
+
+ /* If a verification is forced, we set the cached signature status
+ first to "?" to mark that no verification has yet happened. */
+ if (force)
+ mapi_set_sig_status (message, "?");
+ else if (mapi_has_sig_status (message))
+ return 0; /* Already checked that message. */
+
+ table = mapi_create_attach_table (message, 0);
+ if (!table)
+ return -1; /* No attachment - this should not happen. */
+
+
+ for (i=0; !table[i].end_of_table; i++)
+ if (table[i].attach_type == ATTACHTYPE_MOSS)
+ {
+ moss_idx = i;
+ break;
+ }
+ if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
+ {
+ /* No MOSS flag found in the table but there is only one
+ attachment. Due to the message type we know that this is
+ the original MOSS message. We mark this attachment as
+ hidden, so that it won't get displayed. We further mark it
+ as our original MOSS attachment so that after parsing we have
+ a mean to find it again (see above). */
+ moss_idx = 0;
+ mapi_mark_moss_attach (message, table+0);
+ }
+
+ if (moss_idx == -1)
+ {
+ mapi_release_attach_table (table);
+ return -1; /* No original attachment - this should not happen. */
+ }
+
+
+ inbuf = mapi_get_attach (message, table+0, &inbuflen);
+ if (!inbuf)
+ {
+ mapi_release_attach_table (table);
+ return -1; /* Problem getting the attachment. */
+ }
+
+
+ err = mime_verify (inbuf, inbuflen, message, 0,
+ opt.passwd_ttl, NULL, NULL, 0);
+ log_debug ("mime_verify returned %d", err);
+ xfree (inbuf);
+
+ if (err)
+ mapi_set_sig_status (message, gpg_strerror (err));
+ else
+ mapi_set_sig_status (message, "Signature was good");
+
+ mapi_release_attach_table (table);
+ return 0;
+}
+
+/* Decrypt MESSAGE, check signature and update the attachments as
+ required. MSGTYPE should be the type of the message so that the
+ function can decide what to do. With FORCE set the verification is
+ done regardlessless of a cached signature result - hmmm, should we
+ such a thing for an encrypted message? */
+int
+message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
+{
+ mapi_attach_item_t *table;
+ int part2_idx;
+ int tblidx;
+ int retval = -1;
+ LPSTREAM cipherstream;
+ gpg_error_t err;
+ int is_opaque = 0;
+ int is_smime = 0;
+
+ switch (msgtype)
+ {
+ case MSGTYPE_UNKNOWN:
+ case MSGTYPE_GPGOL:
+ case MSGTYPE_GPGOL_OPAQUE_SIGNED:
+ case MSGTYPE_GPGOL_MULTIPART_SIGNED:
+ return -1; /* Should not have been called for this. */
+ case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
+ break;
+ case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
+ is_opaque = 1;
+ break;
+ }
+
+ table = mapi_create_attach_table (message, 0);
+ if (!table)
+ return -1; /* No attachment - this should not happen. */
+
+ if (is_opaque)
+ {
+ /* S/MIME opaque encrypted message: We expect 1 attachment. As
+ we don't know ether we are called the first time, we first
+ try to find this attachment by looking at all attachments.
+ Only if this fails we identify it by its order. */
+ part2_idx = -1;
+ for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+ if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
+ {
+ if (part2_idx == -1 && table[tblidx].content_type
+ && (!strcmp (table[tblidx].content_type,
+ "application/pkcs7-mime")
+ || !strcmp (table[tblidx].content_type,
+ "application/x-pkcs7-mime")))
+ part2_idx = tblidx;
+ }
+ if (part2_idx == -1 && tblidx >= 1)
+ {
+ /* We have attachments but none are marked. Thus we assume
+ that this is the first time we see this message and we
+ will set the mark now if we see appropriate content
+ types. */
+ if (table[0].content_type
+ && (!strcmp (table[0].content_type, "application/pkcs7-mime")
+ || !strcmp (table[0].content_type,
+ "application/x-pkcs7-mime")))
+ part2_idx = 0;
+ if (part2_idx != -1)
+ mapi_mark_moss_attach (message, table+part2_idx);
+ }
+ if (part2_idx == -1)
+ {
+ log_debug ("%s:%s: this is not an S/MIME encrypted message",
+ SRCNAME, __func__);
+ goto leave;
+ }
+ is_smime = 1;
+ }
+ else
+ {
+ /* Multipart/encrypted message: We expect 2 attachments. The
+ first one with the version number and the second one with the
+ ciphertext. As we don't know ether we are called the first
+ time, we first try to find these attachments by looking at
+ all attachments. Only if this fails we identify them by
+ their order (i.e. the first 2 attachments) and mark them as
+ part1 and part2. */
+ int part1_idx;
+
+ part1_idx = part2_idx = -1;
+ for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
+ if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
+ {
+ if (part1_idx == -1 && table[tblidx].content_type
+ && !strcmp (table[tblidx].content_type,
+ "application/pgp-encrypted"))
+ part1_idx = tblidx;
+ else if (part2_idx == -1 && table[tblidx].content_type
+ && !strcmp (table[tblidx].content_type,
+ "application/octet-stream"))
+ part2_idx = tblidx;
+ }
+ if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
+ {
+ /* At least 2 attachments but none are marked. Thus we
+ assume that this is the first time we see this message
+ and we will set the mark now if we see appropriate
+ content types. */
+ if (table[0].content_type
+ && !strcmp (table[0].content_type, "application/pgp-encrypted"))
+ part1_idx = 0;
+ if (table[1].content_type
+ && !strcmp (table[1].content_type, "application/octet-stream"))
+ part2_idx = 1;
+ if (part1_idx != -1 && part2_idx != -1)
+ {
+ mapi_mark_moss_attach (message, table+part1_idx);
+ mapi_mark_moss_attach (message, table+part2_idx);
+ }
+ }
+ if (part1_idx == -1 || part2_idx == -1)
+ {
+ log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
+ SRCNAME, __func__);
+ goto leave;
+ }
+ }
+
+ /* Get the attachment as an allocated buffer and let the mimeparser
+ work on it. */
+ cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
+ if (!cipherstream)
+ goto leave; /* Problem getting the attachment. */
+
+ err = mime_decrypt (cipherstream, message, is_smime, opt.passwd_ttl,
+ NULL, NULL, 0);
+ log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
+ cipherstream->Release ();
+ retval = 0;
+
+ leave:
+ mapi_release_attach_table (table);
+ return retval;
+}
Added: trunk/src/message.h
===================================================================
--- trunk/src/message.h 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/message.h 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,30 @@
+/* message.h - Declarations for message.c
+ * Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+
+int message_verify (LPMESSAGE message, msgtype_t msgtype, int force);
+int message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force);
+
+
+#endif /*MESSAGE_H*/
Added: trunk/src/mimeparser.c
===================================================================
--- trunk/src/mimeparser.c 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/mimeparser.c 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,1104 @@
+/* mimeparser.c - Parse multipart MIME message
+ * Copyright (C) 2005, 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/*
+ EXPLAIN what we are doing here.
+*/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#define COBJMACROS
+#include <windows.h>
+#include <objidl.h> /* For IStream. */
+
+#include <gpgme.h>
+
+#include "mymapi.h"
+#include "mymapitags.h"
+
+#include "rfc822parse.h"
+#include "common.h"
+#include "engine.h"
+#include "mapihelp.h"
+#include "serpent.h"
+#include "mimeparser.h"
+
+/* Define the next to get extra debug message for the MIME parser. */
+#define DEBUG_PARSER 1
+
+#define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
+ SRCNAME, __func__, __LINE__); \
+ } while (0)
+
+static const char oid_mimetag[] =
+ {0x2A, 0x86, 0x48, 0x86, 0xf7, 0x14, 0x03, 0x0a, 0x04};
+
+
+
+/* The maximum length of a line we are able to process. RFC822 allows
+ only for 1000 bytes; thus 2000 seems to be a reasonable value. */
+#define LINEBUFSIZE 2000
+
+
+typedef enum
+ {
+ PROTOCOL_UNKNOWN = 0,
+ PROTOCOL_OPENPGP,
+ PROTOCOL_SMIME
+ }
+protocol_t;
+
+
+/* To keep track of the MIME message structures we use a linked list
+ with each item corresponding to one part. */
+struct mimestruct_item_s;
+typedef struct mimestruct_item_s *mimestruct_item_t;
+struct mimestruct_item_s
+{
+ mimestruct_item_t next;
+ unsigned int level; /* Level in the hierarchy of that part. 0
+ indicates the outer body. */
+ char *filename; /* Malloced fileanme or NULL. */
+ char *charset; /* Malloced charset or NULL. */
+ char content_type[1]; /* String with the content type. */
+};
+
+
+
+
+/* The context object we use to track information. */
+struct mime_context
+{
+ HWND hwnd; /* A window handle to be used for message boxes etc. */
+ rfc822parse_t msg; /* The handle of the RFC822 parser. */
+
+ int preview; /* Do only decryption and pop up no message bozes. */
+
+ int protect_mode; /* Encrypt all attachments etc. (cf. SYMENC). */
+ int verify_mode; /* True if we want to verify a signature. */
+
+ int nesting_level; /* Current MIME nesting level. */
+ int in_data; /* We are currently in data (body or attachment). */
+ int body_seen; /* True if we have seen a part we consider the
+ body of the message. */
+
+ gpgme_data_t signed_data;/* NULL or the data object used to collect
+ the signed data. It would be better to
+ just hash it but there is no support in
+ gpgme for this yet. */
+ gpgme_data_t sig_data; /* NULL or data object to collect the
+ signature attachment which should be a
+ signature then. */
+
+ int collect_attachment; /* True if we are collecting an attachment
+ or the body. */
+ int collect_signeddata; /* True if we are collecting the signed data. */
+ int collect_signature; /* True if we are collecting a signature. */
+ int start_hashing; /* Flag used to start collecting signed data. */
+ int hashing_level; /* MIME level where we started hashing. */
+ int is_qp_encoded; /* Current part is QP encoded. */
+ int is_base64_encoded; /* Current part is base 64 encoded. */
+ int is_utf8; /* Current part has charset utf-8. */
+ protocol_t protocol; /* The detected crypto protocol. */
+
+ int part_counter; /* Counts the number of processed parts. */
+ int any_boundary; /* Indicates whether we have seen any
+ boundary which means that we are actually
+ working on a MIME message and not just on
+ plain rfc822 message. */
+
+ /* A linked list describing the structure of the mime message. This
+ list gets build up while parsing the message. */
+ mimestruct_item_t mimestruct;
+ mimestruct_item_t *mimestruct_tail;
+ mimestruct_item_t mimestruct_cur;
+
+ LPMESSAGE mapi_message; /* The MAPI message object we are working on. */
+ LPSTREAM outstream; /* NULL or a stream to write a part to. */
+ LPATTACH mapi_attach; /* The attachment object we are writing. */
+ symenc_t symenc; /* NULL or the context used to protect
+ attachments. */
+ int any_attachments_created; /* True if we created a new atatchment. */
+
+ b64_state_t base64; /* The state of the Base-64 decoder. */
+
+ int line_too_long; /* Indicates that a received line was too long. */
+ int parser_error; /* Indicates that we encountered a error from
+ the parser. */
+
+ /* Buffer used to constructed complete files. */
+ size_t linebufsize; /* The allocated size of the buffer. */
+ size_t linebufpos; /* The actual write posituion. */
+ char linebuf[1]; /* The buffer. */
+};
+typedef struct mime_context *mime_context_t;
+
+
+/* This function is a wrapper around gpgme_data_write to convert the
+ data to utf-8 first. We assume Latin-1 here. */
+/* static int */
+/* latin1_data_write (gpgme_data_t data, const char *line, size_t len) */
+/* { */
+/* const char *s; */
+/* char *buffer, *p; */
+/* size_t i, n; */
+/* int rc; */
+
+/* for (s=line, i=0, n=0 ; i < len; s++, i++ ) */
+/* { */
+/* n++; */
+/* if (*s & 0x80) */
+/* n++; */
+/* } */
+/* buffer = xmalloc (n + 1); */
+/* for (s=line, i=0, p=buffer; i < len; s++, i++ ) */
+/* { */
+/* if (*s & 0x80) */
+/* { */
+/* *p++ = 0xc0 | ((*s >> 6) & 3); */
+/* *p++ = 0x80 | (*s & 0x3f); */
+/* } */
+/* else */
+/* *p++ = *s; */
+/* } */
+/* assert (p-buffer == n); */
+/* rc = gpgme_data_write (data, buffer, n); */
+/* xfree (buffer); */
+/* return rc; */
+/* } */
+
+
+/* Print the message event EVENT. */
+static void
+debug_message_event (mime_context_t ctx, rfc822parse_event_t event)
+{
+ const char *s;
+
+ switch (event)
+ {
+ case RFC822PARSE_OPEN: s= "Open"; break;
+ case RFC822PARSE_CLOSE: s= "Close"; break;
+ case RFC822PARSE_CANCEL: s= "Cancel"; break;
+ case RFC822PARSE_T2BODY: s= "T2Body"; break;
+ case RFC822PARSE_FINISH: s= "Finish"; break;
+ case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
+ case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
+ case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
+ case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
+ case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
+ case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
+ case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
+ case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
+ default: s= "[unknown event]"; break;
+ }
+#ifdef DEBUG_PARSER
+ log_debug ("%s: ctx=%p, rfc822 event %s\n", SRCNAME, ctx, s);
+#endif
+}
+
+
+
+/* Start a new atatchment. With IS_BODY set, the attachment is
+ actually the body part of the message which is treated in a special
+ way. */
+static int
+start_attachment (mime_context_t ctx, int is_body)
+{
+ int retval = -1;
+ HRESULT hr;
+ ULONG newpos;
+ SPropValue prop;
+ LPATTACH newatt = NULL;
+ LPSTREAM to = NULL;
+ LPUNKNOWN punk;
+
+#ifdef DEBUG_PARSER
+ log_debug ("%s:%s: for ctx=%p is_body=%d", SRCNAME, __func__, ctx, is_body);
+#endif
+
+ /* Just in case something has not been finished, do it here. */
+ if (ctx->outstream)
+ {
+ IStream_Release (ctx->outstream);
+ ctx->outstream = NULL;
+ }
+ if (ctx->mapi_attach)
+ {
+ IAttach_Release (ctx->mapi_attach);
+ ctx->mapi_attach = NULL;
+ }
+ if (ctx->symenc)
+ {
+ symenc_close (ctx->symenc);
+ ctx->symenc = NULL;
+ }
+
+ /* Before we start with the first attachment we need to delete all
+ attachments which might have been created already by a past
+ parser run. */
+ if (!ctx->any_attachments_created)
+ {
+ mapi_attach_item_t *table;
+ int i;
+
+ table = mapi_create_attach_table (ctx->mapi_message, 1);
+ if (table)
+ {
+ for (i=0; !table[i].end_of_table; i++)
+ if (table[i].attach_type == ATTACHTYPE_FROMMOSS)
+ {
+ hr = IMessage_DeleteAttach (ctx->mapi_message,
+ table[i].mapipos,
+ 0, NULL, 0);
+ if (hr)
+ log_error ("%s:%s: DeleteAttach failed: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ }
+ mapi_release_attach_table (table);
+ }
+ ctx->any_attachments_created = 1;
+ }
+
+ /* Now create a new attachment. */
+ hr = IMessage_CreateAttach (ctx->mapi_message, NULL, 0, &newpos, &newatt);
+ if (hr)
+ {
+ log_error ("%s:%s: can't create attachment: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+
+ prop.ulPropTag = PR_ATTACH_METHOD;
+ prop.Value.ul = ATTACH_BY_VALUE;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr != S_OK)
+ {
+ log_error ("%s:%s: can't set attach method: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+
+ /* Mark that attachment so that we know why it has been created. */
+ if (get_gpgolattachtype_tag (ctx->mapi_message, &prop.ulPropTag) )
+ goto leave;
+ prop.Value.l = ATTACHTYPE_FROMMOSS;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr)
+ {
+ log_error ("%s:%s: can't set %s property: hr=%#lx\n",
+ SRCNAME, __func__, "GpgOL Attach Type", hr);
+ goto leave;
+ }
+
+
+ /* The body attachment is special and should not be show in the list
+ of atatchments. */
+ if (is_body)
+ {
+ prop.ulPropTag = PR_ATTACHMENT_HIDDEN;
+ prop.Value.b = TRUE;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr)
+ {
+ log_error ("%s:%s: can't set hidden attach flag: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+ }
+
+
+ /* We need to insert a short filename . Without it, the _displayed_
+ list of attachments won't get updated although the attachment has
+ been created. */
+ prop.ulPropTag = PR_ATTACH_FILENAME_A;
+ {
+ char buf[100];
+
+ if (is_body)
+ prop.Value.lpszA = is_body == 2? "gpgol000.htm":"gpgol000.txt";
+ else
+ {
+ snprintf (buf, 100, "gpgol%03d.dat", ctx->part_counter);
+ prop.Value.lpszA = buf;
+ }
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ }
+ if (hr)
+ {
+ log_error ("%s:%s: can't set attach filename: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+
+ /* And now for the real name. */
+ if (ctx->mimestruct_cur && ctx->mimestruct_cur->filename)
+ {
+ prop.ulPropTag = PR_ATTACH_LONG_FILENAME_A;
+ prop.Value.lpszA = ctx->mimestruct_cur->filename;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr)
+ {
+ log_error ("%s:%s: can't set attach long filename: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+ }
+
+ prop.ulPropTag = PR_ATTACH_TAG;
+ prop.Value.bin.cb = sizeof oid_mimetag;
+ prop.Value.bin.lpb = (LPBYTE)oid_mimetag;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr)
+ {
+ log_error ("%s:%s: can't set attach tag: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+
+ assert (ctx->mimestruct_cur && ctx->mimestruct_cur->content_type);
+ prop.ulPropTag = PR_ATTACH_MIME_TAG_A;
+ prop.Value.lpszA = ctx->mimestruct_cur->content_type;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr)
+ {
+ log_error ("%s:%s: can't set attach mime tag: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+
+
+ /* If we are in protect mode (i.e. working on a decrypted message,
+ we need to setup the symkey context to protect (encrypt) the
+ attachment in the MAPI. */
+ if (ctx->protect_mode)
+ {
+ char *iv;
+
+ if (get_gpgolprotectiv_tag (ctx->mapi_message, &prop.ulPropTag) )
+ goto leave;
+
+ iv = create_initialization_vector (16);
+ if (!iv)
+ {
+ log_error ("%s:%s: error creating initialization vector",
+ SRCNAME, __func__);
+ goto leave;
+ }
+ prop.Value.bin.cb = 16;
+ prop.Value.bin.lpb = iv;
+ hr = HrSetOneProp ((LPMAPIPROP)newatt, &prop);
+ if (hr)
+ {
+ log_error ("%s:%s: can't set %s property: hr=%#lx\n",
+ SRCNAME, __func__, "GpgOL Protect IV", hr);
+ goto leave;
+ }
+
+ ctx->symenc = symenc_open (get_128bit_session_key (), 16, iv, 16);
+ xfree (iv);
+ if (!ctx->symenc)
+ {
+ log_error ("%s:%s: error creating cipher context",
+ SRCNAME, __func__);
+ goto leave;
+ }
+ }
+
+
+ punk = (LPUNKNOWN)to;
+ hr = IAttach_OpenProperty (newatt, PR_ATTACH_DATA_BIN, &IID_IStream, 0,
+ MAPI_CREATE|MAPI_MODIFY, &punk);
+ if (FAILED (hr))
+ {
+ log_error ("%s:%s: can't create output stream: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ goto leave;
+ }
+ to = (LPSTREAM)punk;
+
+ ctx->outstream = to;
+ to = NULL;
+ ctx->mapi_attach = newatt;
+ newatt = NULL;
+
+ if (ctx->symenc)
+ {
+ char tmpbuf[16];
+ /* Write an encrypted fixed 16 byte string which we need to
+ check at decryption time to see whether we have actually
+ encrypted it using this session key. */
+ symenc_cfb_encrypt (ctx->symenc, tmpbuf, "GpgOL attachment", 16);
+ IStream_Write (ctx->outstream, tmpbuf, 16, NULL);
+ }
+ retval = 0; /* Success. */
+
+ leave:
+ if (to)
+ {
+ IStream_Revert (to);
+ IStream_Release (to);
+ }
+ if (newatt)
+ IAttach_Release (newatt);
+ return retval;
+}
+
+
+static int
+finish_attachment (mime_context_t ctx, int cancel)
+{
+ HRESULT hr;
+ int retval = -1;
+
+#ifdef DEBUG_PARSER
+ log_debug ("%s:%s: for ctx=%p cancel=%d", SRCNAME, __func__, ctx, cancel);
+#endif
+
+ if (ctx->outstream)
+ {
+ IStream_Commit (ctx->outstream, 0);
+ IStream_Release (ctx->outstream);
+ ctx->outstream = NULL;
+
+ if (cancel)
+ retval = 0;
+ else if (ctx->mapi_attach)
+ {
+ hr = IAttach_SaveChanges (ctx->mapi_attach, KEEP_OPEN_READWRITE);
+ if (hr)
+ {
+ log_error ("%s:%s: SaveChanges(attachment) failed: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ }
+ else
+ retval = 0; /* Success. */
+ }
+ }
+ if (ctx->mapi_attach)
+ {
+ IAttach_Release (ctx->mapi_attach);
+ ctx->mapi_attach = NULL;
+ }
+ if (ctx->symenc)
+ {
+ symenc_close (ctx->symenc);
+ ctx->symenc = NULL;
+ }
+ return retval;
+}
+
+
+/* Process the transition to body event.
+
+ This means we have received the empty line indicating the body and
+ should now check the headers to see what to do about this part. */
+static int
+t2body (mime_context_t ctx, rfc822parse_t msg)
+{
+ rfc822parse_field_t field;
+ const char *ctmain, *ctsub;
+ const char *s;
+ size_t off;
+ char *p;
+ int is_text = 0;
+ int is_body = 0;
+ char *filename = NULL;
+ char *charset = NULL;
+
+
+ /* Figure out the encoding. */
+ ctx->is_qp_encoded = 0;
+ ctx->is_base64_encoded = 0;
+ p = rfc822parse_get_field (msg, "Content-Transfer-Encoding", -1, &off);
+ if (p)
+ {
+ if (!stricmp (p+off, "quoted-printable"))
+ ctx->is_qp_encoded = 1;
+ else if (!stricmp (p+off, "base64"))
+ {
+ ctx->is_base64_encoded = 1;
+ b64_init (&ctx->base64);
+ }
+ free (p);
+ }
+
+ /* Get the filename from the header. */
+ field = rfc822parse_parse_field (msg, "Content-Disposition", -1);
+ if (field)
+ {
+ s = rfc822parse_query_parameter (field, "filename", 0);
+ if (s)
+ filename = xstrdup (s);
+ rfc822parse_release_field (field);
+ }
+
+ /* Process the Content-type and all its parameters. */
+ ctmain = ctsub = NULL;
+ field = rfc822parse_parse_field (msg, "Content-Type", -1);
+ if (field)
+ ctmain = rfc822parse_query_media_type (field, &ctsub);
+ if (!ctmain)
+ {
+ /* Either there is no content type field or it is faulty; in
+ both cases we fall back to text/plain. */
+ ctmain = "text";
+ ctsub = "plain";
+ }
+
+#ifdef DEBUG_PARSER
+ log_debug ("%s:%s: ctx=%p, ct=`%s/%s'\n",
+ SRCNAME, __func__, ctx, ctmain, ctsub);
+#endif
+
+ /* We only support UTF-8 for now. Check here. */
+ s = rfc822parse_query_parameter (field, "charset", 0);
+ if (s)
+ charset = xstrdup (s);
+ ctx->is_utf8 = (s && !strcmp (s, "utf-8"));
+
+ /* Update our idea of the entire MIME structure. */
+ {
+ mimestruct_item_t ms;
+
+ ms = xmalloc (sizeof *ms + strlen (ctmain) + 1 + strlen (ctsub));
+ ctx->mimestruct_cur = ms;
+ *ctx->mimestruct_tail = ms;
+ ctx->mimestruct_tail = &ms->next;
+ ms->next = NULL;
+ strcpy (stpcpy (stpcpy (ms->content_type, ctmain), "/"), ctsub);
+ ms->level = ctx->nesting_level;
+ ms->filename = filename;
+ filename = NULL;
+ ms->charset = charset;
+ charset = NULL;
+
+ }
+
+
+ if (!strcmp (ctmain, "multipart"))
+ {
+ /* We don't care about the top level multipart layer but wait
+ until it comes to the actual parts which then will get stored
+ as attachments.
+
+ For now encapsulated signed or encrypted containers are not
+ processed in a special way as they should. Except for the
+ simple verify mode. */
+ if (ctx->verify_mode && !ctx->signed_data
+ && !strcmp (ctsub, "signed")
+ && (s = rfc822parse_query_parameter (field, "protocol", 0)))
+ {
+ if (!strcmp (s, "application/pgp-signature"))
+ ctx->protocol = PROTOCOL_OPENPGP;
+ else if (!strcmp (s, "application/pkcs7-signature")
+ || !strcmp (s, "application/x-pkcs7-signature"))
+ ctx->protocol = PROTOCOL_SMIME;
+ else
+ ctx->protocol = PROTOCOL_UNKNOWN;
+
+ /* Need to start the hashing after the next boundary. */
+ ctx->start_hashing = 1;
+ }
+ }
+ else if (!strcmp (ctmain, "text"))
+ {
+ is_text = !strcmp (ctsub, "html")? 2:1;
+ }
+ else if (ctx->verify_mode && ctx->nesting_level == 1 && !ctx->sig_data
+ && !strcmp (ctmain, "application")
+ && ((ctx->protocol == PROTOCOL_OPENPGP
+ && !strcmp (ctsub, "pgp-signature"))
+ || (ctx->protocol == PROTOCOL_SMIME
+ && (!strcmp (ctsub, "pkcs7-signature")
+ || !strcmp (ctsub, "x-pkcs7-signature")))))
+ {
+ /* This is the second part of a MOSS signature. We only support
+ here full messages thus checking the nesting level is
+ sufficient. We do this only for the first signature (i.e. if
+ sig_data has not been set yet). We also do this only while
+ in verify mode because we don't want to write a full MUA. */
+ if (!ctx->preview && !gpgme_data_new (&ctx->sig_data))
+ ctx->collect_signature = 1;
+ }
+ else /* Other type. */
+ {
+ if (!ctx->preview)
+ ctx->collect_attachment = 1;
+ }
+ rfc822parse_release_field (field); /* (Content-type) */
+ ctx->in_data = 1;
+
+#ifdef DEBUG_PARSER
+ log_debug ("%s: this body: nesting=%d part_counter=%d is_text=%d\n",
+ SRCNAME, ctx->nesting_level, ctx->part_counter, is_text);
+#endif
+
+ /* If this is a text part, decide whether we treat it as our body. */
+ if (is_text)
+ {
+ /* If this is the first text part at all we will start to
+ collect it and use it later as the regular body. */
+ if (!ctx->body_seen)
+ {
+ ctx->body_seen = 1;
+ ctx->collect_attachment = 1;
+ is_body = 1;
+ }
+ else if (!ctx->preview)
+ ctx->collect_attachment = 1;
+ }
+
+
+ if (ctx->collect_attachment)
+ {
+ /* Now that if we have an attachment prepare a new MAPI
+ attachment. */
+ if (start_attachment (ctx, is_body))
+ return -1;
+ assert (ctx->outstream);
+ }
+
+ return 0;
+}
+
+
+/* This routine gets called by the RFC822 parser for all kind of
+ events. OPAQUE carries in our case an smime context. Should
+ return 0 on success or -1 as well as setting errno on
+ failure. */
+static int
+message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
+{
+ int retval = 0;
+ mime_context_t ctx = opaque;
+
+ debug_message_event (ctx, event);
+
+ if (event == RFC822PARSE_BEGIN_HEADER || event == RFC822PARSE_T2BODY)
+ {
+ /* We need to check here whether to start collecting signed data
+ because attachments might come without header lines and thus
+ we won't see the BEGIN_HEADER event. */
+ if (ctx->start_hashing == 1)
+ {
+ ctx->start_hashing = 2;
+ ctx->hashing_level = ctx->nesting_level;
+ ctx->collect_signeddata = 1;
+ gpgme_data_new (&ctx->signed_data);
+ }
+ }
+
+
+ switch (event)
+ {
+ case RFC822PARSE_T2BODY:
+ retval = t2body (ctx, msg);
+ break;
+
+ case RFC822PARSE_LEVEL_DOWN:
+ ctx->nesting_level++;
+ break;
+
+ case RFC822PARSE_LEVEL_UP:
+ if (ctx->nesting_level)
+ ctx->nesting_level--;
+ else
+ {
+ log_error ("%s: ctx=%p, invalid structure: bad nesting level\n",
+ SRCNAME, ctx);
+ ctx->parser_error = 1;
+ }
+ break;
+
+ case RFC822PARSE_BOUNDARY:
+ case RFC822PARSE_LAST_BOUNDARY:
+ ctx->any_boundary = 1;
+ ctx->in_data = 0;
+ ctx->collect_attachment = 0;
+
+ finish_attachment (ctx, 0);
+ assert (!ctx->outstream);
+
+ if (ctx->start_hashing == 2 && ctx->hashing_level == ctx->nesting_level)
+ {
+ ctx->start_hashing = 3; /* Avoid triggering it again. */
+ ctx->collect_signeddata = 0;
+ }
+ break;
+
+ case RFC822PARSE_BEGIN_HEADER:
+ ctx->part_counter++;
+ break;
+
+ default: /* Ignore all other events. */
+ break;
+ }
+
+ return retval;
+}
+
+
+
+/* This handler is called by GPGME with the decrypted plaintext. */
+static ssize_t
+plaintext_handler (void *handle, const void *buffer, size_t size)
+{
+ mime_context_t ctx = handle;
+ const char *s;
+ size_t nleft, pos, len;
+
+ s = buffer;
+ pos = ctx->linebufpos;
+ nleft = size;
+ for (; nleft ; nleft--, s++)
+ {
+ if (pos >= ctx->linebufsize)
+ {
+ log_error ("%s: ctx=%p, rfc822 parser failed: line too long\n",
+ SRCNAME, ctx);
+ ctx->line_too_long = 1;
+ return 0; /* Error. */
+ }
+ if (*s != '\n')
+ ctx->linebuf[pos++] = *s;
+ else
+ { /* Got a complete line. Remove the last CR */
+ if (pos && ctx->linebuf[pos-1] == '\r')
+ pos--;
+
+ if (rfc822parse_insert (ctx->msg, ctx->linebuf, pos))
+ {
+ log_error ("%s: ctx=%p, rfc822 parser failed: %s\n",
+ SRCNAME, ctx, strerror (errno));
+ ctx->parser_error = 1;
+ return 0; /* Error. */
+ }
+
+
+ if (ctx->collect_signeddata && ctx->signed_data)
+ {
+ /* Save the signed data. Note that we need to delay
+ the CR/LF because the last line ending belongs to the
+ next boundary. */
+ if (ctx->collect_signeddata == 2)
+ gpgme_data_write (ctx->signed_data, "\r\n", 2);
+ gpgme_data_write (ctx->signed_data, ctx->linebuf, pos);
+ ctx->collect_signeddata = 2;
+ }
+
+ if (ctx->in_data && ctx->collect_attachment)
+ {
+ /* We are inside of an attachment part. Write it out. */
+ if (ctx->collect_attachment == 1) /* Skip the first line. */
+ ctx->collect_attachment = 2;
+ else if (ctx->outstream)
+ {
+ HRESULT hr = 0;
+
+ if (ctx->is_qp_encoded)
+ len = qp_decode (ctx->linebuf, pos);
+ else if (ctx->is_base64_encoded)
+ len = b64_decode (&ctx->base64, ctx->linebuf, pos);
+ else
+ len = pos;
+ if (len)
+ {
+ if (ctx->symenc)
+ symenc_cfb_encrypt (ctx->symenc, ctx->linebuf,
+ ctx->linebuf, len);
+ hr = IStream_Write (ctx->outstream, ctx->linebuf,
+ len, NULL);
+ }
+ if (!hr && !ctx->is_base64_encoded)
+ {
+ char tmp[3] = "\r\n";
+
+ if (ctx->symenc)
+ symenc_cfb_encrypt (ctx->symenc, tmp, tmp, 2);
+ hr = IStream_Write (ctx->outstream, tmp, 2, NULL);
+ }
+ if (hr)
+ {
+ log_debug ("%s:%s: Write failed: hr=%#lx",
+ SRCNAME, __func__, hr);
+ if (!ctx->preview)
+ MessageBox (ctx->hwnd, _("Error writing to stream"),
+ _("I/O-Error"), MB_ICONERROR|MB_OK);
+ ctx->parser_error = 1;
+ return 0; /* Error. */
+ }
+ }
+ }
+ else if (ctx->in_data && ctx->collect_signature)
+ {
+ /* We are inside of a signature attachment part. */
+ if (ctx->collect_signature == 1) /* Skip the first line. */
+ ctx->collect_signature = 2;
+ else if (ctx->sig_data)
+ {
+ if (ctx->is_qp_encoded)
+ len = qp_decode (ctx->linebuf, pos);
+ else if (ctx->is_base64_encoded)
+ len = b64_decode (&ctx->base64, ctx->linebuf, pos);
+ else
+ len = pos;
+ if (len)
+ gpgme_data_write (ctx->sig_data, ctx->linebuf, len);
+ if (!ctx->is_base64_encoded)
+ gpgme_data_write (ctx->sig_data, "\r\n", 2);
+ }
+ }
+
+ /* Continue with next line. */
+ pos = 0;
+ }
+ }
+ ctx->linebufpos = pos;
+
+ return size;
+}
+
+
+
+static void
+show_mimestruct (mimestruct_item_t mimestruct)
+{
+ mimestruct_item_t ms;
+
+ for (ms = mimestruct; ms; ms = ms->next)
+ log_debug ("MIMESTRUCT: %*s%s cs=%s fn=%s\n",
+ ms->level*2, "", ms->content_type,
+ ms->charset? ms->charset : "[none]",
+ ms->filename? ms->filename : "[none]");
+}
+
+
+
+int
+mime_verify (const char *message, size_t messagelen,
+ LPMESSAGE mapi_message, int is_smime,
+ int ttl, gpgme_data_t attestation, HWND hwnd, int preview_mode)
+{
+ gpg_error_t err = 0;
+ mime_context_t ctx;
+ const char *s;
+ size_t len;
+
+ (void)is_smime; /* Not yet used. */
+
+ log_debug ("%s:%s: enter", SRCNAME, __func__);
+
+ ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
+ ctx->linebufsize = LINEBUFSIZE;
+ ctx->hwnd = hwnd;
+ ctx->preview = preview_mode;
+ ctx->verify_mode = 1;
+ ctx->mapi_message = mapi_message;
+ ctx->mimestruct_tail = &ctx->mimestruct;
+
+ ctx->msg = rfc822parse_open (message_cb, ctx);
+ if (!ctx->msg)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("%s:%s: failed to open the RFC822 parser: %s",
+ SRCNAME, __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Need to pass the data line by line to the handler. */
+ while ( (s = memchr (message, '\n', messagelen)) )
+ {
+ len = s - message + 1;
+ plaintext_handler (ctx, message, len);
+ if (ctx->parser_error || ctx->line_too_long)
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ break;
+ }
+ message += len;
+ assert (messagelen >= len);
+ messagelen -= len;
+ }
+ /* Note: the last character should be a LF, if not we ignore such an
+ incomplete last line. */
+
+
+ /* Now actually verify the signature. */
+ if (!err && ctx->signed_data && ctx->sig_data)
+ {
+ char *tmp;
+ gpgme_protocol_t xprot;
+ int inv_prot = 0;
+
+ gpgme_data_seek (ctx->signed_data, 0, SEEK_SET);
+ gpgme_data_seek (ctx->sig_data, 0, SEEK_SET);
+ if (ctx->protocol == PROTOCOL_OPENPGP)
+ {
+ tmp = native_to_utf8 (_("[OpenPGP signature]"));
+ xprot = GPGME_PROTOCOL_OpenPGP;
+ }
+ else if (ctx->protocol == PROTOCOL_SMIME)
+ {
+ tmp = native_to_utf8 (_("[S/MIME signature]"));
+ xprot = GPGME_PROTOCOL_CMS;
+ }
+ else
+ {
+ tmp = native_to_utf8 (_("[Unknown signature protocol]"));
+ inv_prot = 1;
+ }
+
+ err = (inv_prot
+ ? gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL)
+ : op_verify_detached_sig_gpgme (xprot,
+ ctx->signed_data, ctx->sig_data,
+ tmp, attestation));
+ log_debug ("%s:%s: checked signature: %s <%s>",
+ SRCNAME, __func__, gpg_strerror (err), op_strsource (err));
+ xfree (tmp);
+ }
+
+
+ leave:
+ if (ctx)
+ {
+ /* Cancel any left open attachment. */
+ finish_attachment (ctx, 1);
+ rfc822parse_close (ctx->msg);
+ if (ctx->signed_data)
+ gpgme_data_release (ctx->signed_data);
+ if (ctx->sig_data)
+ gpgme_data_release (ctx->sig_data);
+ show_mimestruct (ctx->mimestruct);
+ while (ctx->mimestruct)
+ {
+ mimestruct_item_t tmp = ctx->mimestruct->next;
+ xfree (ctx->mimestruct->filename);
+ xfree (ctx->mimestruct->charset);
+ xfree (ctx->mimestruct);
+ ctx->mimestruct = tmp;
+ }
+ symenc_close (ctx->symenc);
+ xfree (ctx);
+ }
+ return err;
+}
+
+
+
+/* Decrypt the PGP or S/MIME message taken from INSTREAM. If
+ ATTESTATION is not NULL a text with the result of the signature
+ verification will get printed to it. HWND is the window to be used
+ for message box and such. In PREVIEW_MODE no verification will be
+ done, no messages saved and no messages boxes will pop up. */
+int
+mime_decrypt (LPSTREAM instream, LPMESSAGE mapi_message, int is_smime,
+ int ttl, gpgme_data_t attestation, HWND hwnd, int preview_mode)
+{
+ gpg_error_t err;
+ struct gpgme_data_cbs cbs;
+ gpgme_data_t plaintext;
+ mime_context_t ctx;
+ char *title;
+ gpgme_protocol_t proto;
+
+ log_debug ("%s:%s: enter", SRCNAME, __func__);
+
+ memset (&cbs, 0, sizeof cbs);
+ cbs.write = plaintext_handler;
+
+ ctx = xcalloc (1, sizeof *ctx + LINEBUFSIZE);
+ ctx->linebufsize = LINEBUFSIZE;
+ ctx->protect_mode = 1;
+ ctx->hwnd = hwnd;
+ ctx->preview = preview_mode;
+ ctx->mapi_message = mapi_message;
+ ctx->mimestruct_tail = &ctx->mimestruct;
+
+ ctx->msg = rfc822parse_open (message_cb, ctx);
+ if (!ctx->msg)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("%s:%s: failed to open the RFC822 parser: %s",
+ SRCNAME, __func__, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = gpgme_data_new_from_cbs (&plaintext, &cbs, ctx);
+ if (err)
+ goto leave;
+
+ if (is_smime)
+ {
+ proto = GPGME_PROTOCOL_CMS;
+ title = native_to_utf8 (_("[Encrypted S/MIME message]"));
+ }
+ else
+ {
+ proto = GPGME_PROTOCOL_OpenPGP;
+ title = native_to_utf8 (_("[Encrypted PGP/MIME message]"));
+ }
+ err = op_decrypt_stream_to_gpgme (proto, instream, plaintext, ttl, title,
+ attestation, preview_mode);
+ xfree (title);
+ if (!err && (ctx->parser_error || ctx->line_too_long))
+ err = gpg_error (GPG_ERR_GENERAL);
+
+
+ leave:
+ if (plaintext)
+ gpgme_data_release (plaintext);
+ if (ctx)
+ {
+ /* Cancel any left over attachment which means that the MIME
+ structure was not complete. However if we have not seen any
+ boundary the message is a non-MIME one but we way have
+ started the body attachment (gpgol000.txt) - this one needs
+ to be finished properly. */
+ finish_attachment (ctx, ctx->any_boundary? 1: 0);
+ rfc822parse_close (ctx->msg);
+ if (ctx->signed_data)
+ gpgme_data_release (ctx->signed_data);
+ if (ctx->sig_data)
+ gpgme_data_release (ctx->sig_data);
+ show_mimestruct (ctx->mimestruct);
+ while (ctx->mimestruct)
+ {
+ mimestruct_item_t tmp = ctx->mimestruct->next;
+ xfree (ctx->mimestruct->filename);
+ xfree (ctx->mimestruct->charset);
+ xfree (ctx->mimestruct);
+ ctx->mimestruct = tmp;
+ }
+ symenc_close (ctx->symenc);
+ xfree (ctx);
+ }
+ return err;
+}
+
Added: trunk/src/mimeparser.h
===================================================================
--- trunk/src/mimeparser.h 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/mimeparser.h 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,44 @@
+/* mimeparse.h - Multipart MIME parser.
+ * Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef SMIME_H
+#define SMIME_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+
+int mime_verify (const char *message, size_t messagelen,
+ LPMESSAGE mapi_message, int is_smime,
+ int ttl,
+ gpgme_data_t attestation, HWND hwnd, int preview_mode);
+int mime_decrypt (LPSTREAM instream, LPMESSAGE mapi_message, int is_smime,
+ int ttl,
+ gpgme_data_t attestation, HWND hwnd, int preview_mode);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*SMIME_H*/
Added: trunk/src/serpent.c
===================================================================
--- trunk/src/serpent.c 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/serpent.c 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,1116 @@
+/* serpent.c - Implementation of the Serpent encryption algorithm.
+ * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt 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.
+ *
+ * Libgcrypt 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ Requirements:
+
+ Big endian machines need to define WORDS_BIGENDIAN
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "serpent.h"
+
+/* Serpent is a 128 bit block cipher. */
+#define BLOCKSIZE 16
+
+/* Number of rounds per Serpent encrypt/decrypt operation. */
+#define ROUNDS 32
+
+/* Magic number, used during generating of the subkeys. */
+#define PHI 0x9E3779B9
+
+/* Serpent works on 128 bit blocks. */
+typedef uint32_t serpent_block_t[4];
+
+/* Serpent key, provided by the user. If the original key is shorter
+ than 256 bits, it is padded. */
+typedef uint32_t serpent_key_t[8];
+
+/* The key schedule consists of 33 128 bit subkeys. */
+typedef uint32_t serpent_subkeys_t[ROUNDS + 1][4];
+
+/* A Serpent context. Note that the public API uses symenc_t
+ instead. */
+struct symenc_context_s
+{
+ serpent_subkeys_t keys; /* Generated subkeys. */
+
+ union {
+ uint32_t dummy; /* Force alignment. */
+ struct {
+ unsigned char iv[BLOCKSIZE]; /* The IV. */
+ unsigned int unused; /* Unused bytes in the IV. */
+ } cfb;
+ } u;
+};
+typedef struct symenc_context_s serpent_context_t;
+
+
+/* A prototype. */
+static const char *serpent_test (void);
+
+#define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
+#define ror(x,n) ( ((x) >> (n)) | ((x) << (32-(n))) )
+
+#define byte_swap_32(x) \
+ (0 \
+ | (((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) \
+ | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+
+/* These are the S-Boxes of Serpent. They are copied from Serpents
+ reference implementation (the optimized one, contained in
+ `floppy2') and are therefore:
+
+ Copyright (C) 1998 Ross Anderson, Eli Biham, Lars Knudsen.
+
+ To quote the Serpent homepage
+ (http://www.cl.cam.ac.uk/~rja14/serpent.html):
+
+ "Serpent is now completely in the public domain, and we impose no
+ restrictions on its use. This was announced on the 21st August at
+ the First AES Candidate Conference. The optimised implementations
+ in the submission package are now under the GNU PUBLIC LICENSE
+ (GPL), although some comments in the code still say otherwise. You
+ are welcome to use Serpent for any application." */
+
+#define SBOX0(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t05, t06, t07, t08, t09; \
+ uint32_t t11, t12, t13, t14, t15, t17, t01; \
+ t01 = b ^ c ; \
+ t02 = a | d ; \
+ t03 = a ^ b ; \
+ z = t02 ^ t01; \
+ t05 = c | z ; \
+ t06 = a ^ d ; \
+ t07 = b | c ; \
+ t08 = d & t05; \
+ t09 = t03 & t07; \
+ y = t09 ^ t08; \
+ t11 = t09 & y ; \
+ t12 = c ^ d ; \
+ t13 = t07 ^ t11; \
+ t14 = b & t06; \
+ t15 = t06 ^ t13; \
+ w = ~ t15; \
+ t17 = w ^ t14; \
+ x = t12 ^ t17; \
+ }
+
+#define SBOX0_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t08, t09, t10; \
+ uint32_t t12, t13, t14, t15, t17, t18, t01; \
+ t01 = c ^ d ; \
+ t02 = a | b ; \
+ t03 = b | c ; \
+ t04 = c & t01; \
+ t05 = t02 ^ t01; \
+ t06 = a | t04; \
+ y = ~ t05; \
+ t08 = b ^ d ; \
+ t09 = t03 & t08; \
+ t10 = d | y ; \
+ x = t09 ^ t06; \
+ t12 = a | t05; \
+ t13 = x ^ t12; \
+ t14 = t03 ^ t10; \
+ t15 = a ^ c ; \
+ z = t14 ^ t13; \
+ t17 = t05 & t13; \
+ t18 = t14 | t17; \
+ w = t15 ^ t18; \
+ }
+
+#define SBOX1(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t07, t08; \
+ uint32_t t10, t11, t12, t13, t16, t17, t01; \
+ t01 = a | d ; \
+ t02 = c ^ d ; \
+ t03 = ~ b ; \
+ t04 = a ^ c ; \
+ t05 = a | t03; \
+ t06 = d & t04; \
+ t07 = t01 & t02; \
+ t08 = b | t06; \
+ y = t02 ^ t05; \
+ t10 = t07 ^ t08; \
+ t11 = t01 ^ t10; \
+ t12 = y ^ t11; \
+ t13 = b & d ; \
+ z = ~ t10; \
+ x = t13 ^ t12; \
+ t16 = t10 | x ; \
+ t17 = t05 & t16; \
+ w = c ^ t17; \
+ }
+
+#define SBOX1_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t07, t08; \
+ uint32_t t09, t10, t11, t14, t15, t17, t01; \
+ t01 = a ^ b ; \
+ t02 = b | d ; \
+ t03 = a & c ; \
+ t04 = c ^ t02; \
+ t05 = a | t04; \
+ t06 = t01 & t05; \
+ t07 = d | t03; \
+ t08 = b ^ t06; \
+ t09 = t07 ^ t06; \
+ t10 = t04 | t03; \
+ t11 = d & t08; \
+ y = ~ t09; \
+ x = t10 ^ t11; \
+ t14 = a | y ; \
+ t15 = t06 ^ x ; \
+ z = t01 ^ t04; \
+ t17 = c ^ t15; \
+ w = t14 ^ t17; \
+ }
+
+#define SBOX2(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t05, t06, t07, t08; \
+ uint32_t t09, t10, t12, t13, t14, t01; \
+ t01 = a | c ; \
+ t02 = a ^ b ; \
+ t03 = d ^ t01; \
+ w = t02 ^ t03; \
+ t05 = c ^ w ; \
+ t06 = b ^ t05; \
+ t07 = b | t05; \
+ t08 = t01 & t06; \
+ t09 = t03 ^ t07; \
+ t10 = t02 | t09; \
+ x = t10 ^ t08; \
+ t12 = a | d ; \
+ t13 = t09 ^ x ; \
+ t14 = b ^ t13; \
+ z = ~ t09; \
+ y = t12 ^ t14; \
+ }
+
+#define SBOX2_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t06, t07, t08, t09; \
+ uint32_t t10, t11, t12, t15, t16, t17, t01; \
+ t01 = a ^ d ; \
+ t02 = c ^ d ; \
+ t03 = a & c ; \
+ t04 = b | t02; \
+ w = t01 ^ t04; \
+ t06 = a | c ; \
+ t07 = d | w ; \
+ t08 = ~ d ; \
+ t09 = b & t06; \
+ t10 = t08 | t03; \
+ t11 = b & t07; \
+ t12 = t06 & t02; \
+ z = t09 ^ t10; \
+ x = t12 ^ t11; \
+ t15 = c & z ; \
+ t16 = w ^ x ; \
+ t17 = t10 ^ t15; \
+ y = t16 ^ t17; \
+ }
+
+#define SBOX3(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t07, t08; \
+ uint32_t t09, t10, t11, t13, t14, t15, t01; \
+ t01 = a ^ c ; \
+ t02 = a | d ; \
+ t03 = a & d ; \
+ t04 = t01 & t02; \
+ t05 = b | t03; \
+ t06 = a & b ; \
+ t07 = d ^ t04; \
+ t08 = c | t06; \
+ t09 = b ^ t07; \
+ t10 = d & t05; \
+ t11 = t02 ^ t10; \
+ z = t08 ^ t09; \
+ t13 = d | z ; \
+ t14 = a | t07; \
+ t15 = b & t13; \
+ y = t08 ^ t11; \
+ w = t14 ^ t15; \
+ x = t05 ^ t04; \
+ }
+
+#define SBOX3_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t07, t09; \
+ uint32_t t11, t12, t13, t14, t16, t01; \
+ t01 = c | d ; \
+ t02 = a | d ; \
+ t03 = c ^ t02; \
+ t04 = b ^ t02; \
+ t05 = a ^ d ; \
+ t06 = t04 & t03; \
+ t07 = b & t01; \
+ y = t05 ^ t06; \
+ t09 = a ^ t03; \
+ w = t07 ^ t03; \
+ t11 = w | t05; \
+ t12 = t09 & t11; \
+ t13 = a & y ; \
+ t14 = t01 ^ t05; \
+ x = b ^ t12; \
+ t16 = b | t13; \
+ z = t14 ^ t16; \
+ }
+
+#define SBOX4(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t08, t09; \
+ uint32_t t10, t11, t12, t13, t14, t15, t16, t01; \
+ t01 = a | b ; \
+ t02 = b | c ; \
+ t03 = a ^ t02; \
+ t04 = b ^ d ; \
+ t05 = d | t03; \
+ t06 = d & t01; \
+ z = t03 ^ t06; \
+ t08 = z & t04; \
+ t09 = t04 & t05; \
+ t10 = c ^ t06; \
+ t11 = b & c ; \
+ t12 = t04 ^ t08; \
+ t13 = t11 | t03; \
+ t14 = t10 ^ t09; \
+ t15 = a & t05; \
+ t16 = t11 | t12; \
+ y = t13 ^ t08; \
+ x = t15 ^ t16; \
+ w = ~ t14; \
+ }
+
+#define SBOX4_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t07, t09; \
+ uint32_t t10, t11, t12, t13, t15, t01; \
+ t01 = b | d ; \
+ t02 = c | d ; \
+ t03 = a & t01; \
+ t04 = b ^ t02; \
+ t05 = c ^ d ; \
+ t06 = ~ t03; \
+ t07 = a & t04; \
+ x = t05 ^ t07; \
+ t09 = x | t06; \
+ t10 = a ^ t07; \
+ t11 = t01 ^ t09; \
+ t12 = d ^ t04; \
+ t13 = c | t10; \
+ z = t03 ^ t12; \
+ t15 = a ^ t04; \
+ y = t11 ^ t13; \
+ w = t15 ^ t09; \
+ }
+
+#define SBOX5(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t07, t08, t09; \
+ uint32_t t10, t11, t12, t13, t14, t01; \
+ t01 = b ^ d ; \
+ t02 = b | d ; \
+ t03 = a & t01; \
+ t04 = c ^ t02; \
+ t05 = t03 ^ t04; \
+ w = ~ t05; \
+ t07 = a ^ t01; \
+ t08 = d | w ; \
+ t09 = b | t05; \
+ t10 = d ^ t08; \
+ t11 = b | t07; \
+ t12 = t03 | w ; \
+ t13 = t07 | t10; \
+ t14 = t01 ^ t11; \
+ y = t09 ^ t13; \
+ x = t07 ^ t08; \
+ z = t12 ^ t14; \
+ }
+
+#define SBOX5_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t07, t08, t09; \
+ uint32_t t10, t12, t13, t15, t16, t01; \
+ t01 = a & d ; \
+ t02 = c ^ t01; \
+ t03 = a ^ d ; \
+ t04 = b & t02; \
+ t05 = a & c ; \
+ w = t03 ^ t04; \
+ t07 = a & w ; \
+ t08 = t01 ^ w ; \
+ t09 = b | t05; \
+ t10 = ~ b ; \
+ x = t08 ^ t09; \
+ t12 = t10 | t07; \
+ t13 = w | x ; \
+ z = t02 ^ t12; \
+ t15 = t02 ^ t13; \
+ t16 = b ^ d ; \
+ y = t16 ^ t15; \
+ }
+
+#define SBOX6(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t07, t08, t09, t10; \
+ uint32_t t11, t12, t13, t15, t17, t18, t01; \
+ t01 = a & d ; \
+ t02 = b ^ c ; \
+ t03 = a ^ d ; \
+ t04 = t01 ^ t02; \
+ t05 = b | c ; \
+ x = ~ t04; \
+ t07 = t03 & t05; \
+ t08 = b & x ; \
+ t09 = a | c ; \
+ t10 = t07 ^ t08; \
+ t11 = b | d ; \
+ t12 = c ^ t11; \
+ t13 = t09 ^ t10; \
+ y = ~ t13; \
+ t15 = x & t03; \
+ z = t12 ^ t07; \
+ t17 = a ^ b ; \
+ t18 = y ^ t15; \
+ w = t17 ^ t18; \
+ }
+
+#define SBOX6_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t07, t08, t09; \
+ uint32_t t12, t13, t14, t15, t16, t17, t01; \
+ t01 = a ^ c ; \
+ t02 = ~ c ; \
+ t03 = b & t01; \
+ t04 = b | t02; \
+ t05 = d | t03; \
+ t06 = b ^ d ; \
+ t07 = a & t04; \
+ t08 = a | t02; \
+ t09 = t07 ^ t05; \
+ x = t06 ^ t08; \
+ w = ~ t09; \
+ t12 = b & w ; \
+ t13 = t01 & t05; \
+ t14 = t01 ^ t12; \
+ t15 = t07 ^ t13; \
+ t16 = d | t02; \
+ t17 = a ^ x ; \
+ z = t17 ^ t15; \
+ y = t16 ^ t14; \
+ }
+
+#define SBOX7(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t05, t06, t08, t09, t10; \
+ uint32_t t11, t13, t14, t15, t16, t17, t01; \
+ t01 = a & c ; \
+ t02 = ~ d ; \
+ t03 = a & t02; \
+ t04 = b | t01; \
+ t05 = a & b ; \
+ t06 = c ^ t04; \
+ z = t03 ^ t06; \
+ t08 = c | z ; \
+ t09 = d | t05; \
+ t10 = a ^ t08; \
+ t11 = t04 & z ; \
+ x = t09 ^ t10; \
+ t13 = b ^ x ; \
+ t14 = t01 ^ x ; \
+ t15 = c ^ t05; \
+ t16 = t11 | t13; \
+ t17 = t02 | t14; \
+ w = t15 ^ t17; \
+ y = a ^ t16; \
+ }
+
+#define SBOX7_INVERSE(a, b, c, d, w, x, y, z) \
+ { \
+ uint32_t t02, t03, t04, t06, t07, t08, t09; \
+ uint32_t t10, t11, t13, t14, t15, t16, t01; \
+ t01 = a & b ; \
+ t02 = a | b ; \
+ t03 = c | t01; \
+ t04 = d & t02; \
+ z = t03 ^ t04; \
+ t06 = b ^ t04; \
+ t07 = d ^ z ; \
+ t08 = ~ t07; \
+ t09 = t06 | t08; \
+ t10 = b ^ d ; \
+ t11 = a | d ; \
+ x = a ^ t09; \
+ t13 = c ^ t06; \
+ t14 = c & t11; \
+ t15 = d | x ; \
+ t16 = t01 | t10; \
+ w = t13 ^ t15; \
+ y = t14 ^ t16; \
+ }
+
+/* XOR BLOCK1 into BLOCK0. */
+#define BLOCK_XOR(block0, block1) \
+ { \
+ block0[0] ^= block1[0]; \
+ block0[1] ^= block1[1]; \
+ block0[2] ^= block1[2]; \
+ block0[3] ^= block1[3]; \
+ }
+
+/* Copy BLOCK_SRC to BLOCK_DST. */
+#define BLOCK_COPY(block_dst, block_src) \
+ { \
+ block_dst[0] = block_src[0]; \
+ block_dst[1] = block_src[1]; \
+ block_dst[2] = block_src[2]; \
+ block_dst[3] = block_src[3]; \
+ }
+
+/* Apply SBOX number WHICH to to the block found in ARRAY0 at index
+ INDEX, writing the output to the block found in ARRAY1 at index
+ INDEX. */
+#define SBOX(which, array0, array1, index) \
+ SBOX##which (array0[index + 0], array0[index + 1], \
+ array0[index + 2], array0[index + 3], \
+ array1[index + 0], array1[index + 1], \
+ array1[index + 2], array1[index + 3]);
+
+/* Apply inverse SBOX number WHICH to to the block found in ARRAY0 at
+ index INDEX, writing the output to the block found in ARRAY1 at
+ index INDEX. */
+#define SBOX_INVERSE(which, array0, array1, index) \
+ SBOX##which##_INVERSE (array0[index + 0], array0[index + 1], \
+ array0[index + 2], array0[index + 3], \
+ array1[index + 0], array1[index + 1], \
+ array1[index + 2], array1[index + 3]);
+
+/* Apply the linear transformation to BLOCK. */
+#define LINEAR_TRANSFORMATION(block) \
+ { \
+ block[0] = rol (block[0], 13); \
+ block[2] = rol (block[2], 3); \
+ block[1] = block[1] ^ block[0] ^ block[2]; \
+ block[3] = block[3] ^ block[2] ^ (block[0] << 3); \
+ block[1] = rol (block[1], 1); \
+ block[3] = rol (block[3], 7); \
+ block[0] = block[0] ^ block[1] ^ block[3]; \
+ block[2] = block[2] ^ block[3] ^ (block[1] << 7); \
+ block[0] = rol (block[0], 5); \
+ block[2] = rol (block[2], 22); \
+ }
+
+/* Apply the inverse linear transformation to BLOCK. */
+#define LINEAR_TRANSFORMATION_INVERSE(block) \
+ { \
+ block[2] = ror (block[2], 22); \
+ block[0] = ror (block[0] , 5); \
+ block[2] = block[2] ^ block[3] ^ (block[1] << 7); \
+ block[0] = block[0] ^ block[1] ^ block[3]; \
+ block[3] = ror (block[3], 7); \
+ block[1] = ror (block[1], 1); \
+ block[3] = block[3] ^ block[2] ^ (block[0] << 3); \
+ block[1] = block[1] ^ block[0] ^ block[2]; \
+ block[2] = ror (block[2], 3); \
+ block[0] = ror (block[0], 13); \
+ }
+
+/* Apply a Serpent round to BLOCK, using the SBOX number WHICH and the
+ subkeys contained in SUBKEYS. Use BLOCK_TMP as temporary storage.
+ This macro increments `round'. */
+#define ROUND(which, subkeys, block, block_tmp) \
+ { \
+ BLOCK_XOR (block, subkeys[round]); \
+ round++; \
+ SBOX (which, block, block_tmp, 0); \
+ LINEAR_TRANSFORMATION (block_tmp); \
+ BLOCK_COPY (block, block_tmp); \
+ }
+
+/* Apply the last Serpent round to BLOCK, using the SBOX number WHICH
+ and the subkeys contained in SUBKEYS. Use BLOCK_TMP as temporary
+ storage. The result will be stored in BLOCK_TMP. This macro
+ increments `round'. */
+#define ROUND_LAST(which, subkeys, block, block_tmp) \
+ { \
+ BLOCK_XOR (block, subkeys[round]); \
+ round++; \
+ SBOX (which, block, block_tmp, 0); \
+ BLOCK_XOR (block_tmp, subkeys[round]); \
+ round++; \
+ }
+
+/* Apply an inverse Serpent round to BLOCK, using the SBOX number
+ WHICH and the subkeys contained in SUBKEYS. Use BLOCK_TMP as
+ temporary storage. This macro increments `round'. */
+#define ROUND_INVERSE(which, subkey, block, block_tmp) \
+ { \
+ LINEAR_TRANSFORMATION_INVERSE (block); \
+ SBOX_INVERSE (which, block, block_tmp, 0); \
+ BLOCK_XOR (block_tmp, subkey[round]); \
+ round--; \
+ BLOCK_COPY (block, block_tmp); \
+ }
+
+/* Apply the first Serpent round to BLOCK, using the SBOX number WHICH
+ and the subkeys contained in SUBKEYS. Use BLOCK_TMP as temporary
+ storage. The result will be stored in BLOCK_TMP. This macro
+ increments `round'. */
+#define ROUND_FIRST_INVERSE(which, subkeys, block, block_tmp) \
+ { \
+ BLOCK_XOR (block, subkeys[round]); \
+ round--; \
+ SBOX_INVERSE (which, block, block_tmp, 0); \
+ BLOCK_XOR (block_tmp, subkeys[round]); \
+ round--; \
+ }
+
+/* Convert the user provided key KEY of KEY_LENGTH bytes into the
+ internally used format. */
+static void
+serpent_key_prepare (const unsigned char *key, unsigned int key_length,
+ serpent_key_t key_prepared)
+{
+ unsigned int i;
+
+ /* Copy key. */
+ for (i = 0; i < key_length / 4; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ key_prepared[i] = byte_swap_32 (((uint32_t *) key)[i]);
+#else
+ key_prepared[i] = ((uint32_t *) key)[i];
+#endif
+ }
+
+ if (i < 8)
+ {
+ /* Key must be padded according to the Serpent
+ specification. */
+ key_prepared[i] = 0x00000001;
+
+ for (i++; i < 8; i++)
+ key_prepared[i] = 0;
+ }
+}
+
+/* Derive the 33 subkeys from KEY and store them in SUBKEYS. */
+static void
+serpent_subkeys_generate (serpent_key_t key, serpent_subkeys_t subkeys)
+{
+ uint32_t w_real[140]; /* The `prekey'. */
+ uint32_t k[132];
+ uint32_t *w = &w_real[8];
+ int i, j;
+
+ /* Initialize with key values. */
+ for (i = 0; i < 8; i++)
+ w[i - 8] = key[i];
+
+ /* Expand to intermediate key using the affine recurrence. */
+ for (i = 0; i < 132; i++)
+ w[i] = rol (w[i - 8] ^ w[i - 5] ^ w[i - 3] ^ w[i - 1] ^ PHI ^ i, 11);
+
+ /* Calculate subkeys via S-Boxes, in bitslice mode. */
+ SBOX (3, w, k, 0);
+ SBOX (2, w, k, 4);
+ SBOX (1, w, k, 8);
+ SBOX (0, w, k, 12);
+ SBOX (7, w, k, 16);
+ SBOX (6, w, k, 20);
+ SBOX (5, w, k, 24);
+ SBOX (4, w, k, 28);
+ SBOX (3, w, k, 32);
+ SBOX (2, w, k, 36);
+ SBOX (1, w, k, 40);
+ SBOX (0, w, k, 44);
+ SBOX (7, w, k, 48);
+ SBOX (6, w, k, 52);
+ SBOX (5, w, k, 56);
+ SBOX (4, w, k, 60);
+ SBOX (3, w, k, 64);
+ SBOX (2, w, k, 68);
+ SBOX (1, w, k, 72);
+ SBOX (0, w, k, 76);
+ SBOX (7, w, k, 80);
+ SBOX (6, w, k, 84);
+ SBOX (5, w, k, 88);
+ SBOX (4, w, k, 92);
+ SBOX (3, w, k, 96);
+ SBOX (2, w, k, 100);
+ SBOX (1, w, k, 104);
+ SBOX (0, w, k, 108);
+ SBOX (7, w, k, 112);
+ SBOX (6, w, k, 116);
+ SBOX (5, w, k, 120);
+ SBOX (4, w, k, 124);
+ SBOX (3, w, k, 128);
+
+ /* Renumber subkeys. */
+ for (i = 0; i < ROUNDS + 1; i++)
+ for (j = 0; j < 4; j++)
+ subkeys[i][j] = k[4 * i + j];
+}
+
+/* Initialize CONTEXT with the key KEY of KEY_LENGTH bits. */
+static void
+serpent_setkey_internal (serpent_context_t *context,
+ const unsigned char *key, unsigned int key_length)
+{
+ serpent_key_t key_prepared;
+
+ serpent_key_prepare (key, key_length, key_prepared);
+ serpent_subkeys_generate (key_prepared, context->keys);
+}
+
+
+/* Initialize CTX with the key KEY of KEY_LENGTH bytes. */
+static int
+serpent_setkey (serpent_context_t *context,
+ const void *key, unsigned int key_length)
+{
+ static const char *serpent_test_ret;
+ static int serpent_init_done;
+ int ret = 0;
+
+ if (!serpent_init_done)
+ {
+ /* Execute a self-test the first time, Serpent is used. */
+ serpent_test_ret = serpent_test ();
+#ifdef TEST
+ if (serpent_test_ret)
+ fprintf (stderr, "Serpent test failure: %s\n", serpent_test_ret);
+#endif /*TEST*/
+ serpent_init_done = 1;
+ }
+
+ if (serpent_test_ret)
+ ret = -1;
+ else
+ serpent_setkey_internal (context, key, key_length);
+
+ return ret;
+}
+
+
+static void
+serpent_encrypt_internal (serpent_context_t *context,
+ const serpent_block_t input, serpent_block_t output)
+{
+ serpent_block_t b, b_next;
+ int round = 0;
+
+#ifdef WORDS_BIGENDIAN
+ b[0] = byte_swap_32 (input[0]);
+ b[1] = byte_swap_32 (input[1]);
+ b[2] = byte_swap_32 (input[2]);
+ b[3] = byte_swap_32 (input[3]);
+#else
+ b[0] = input[0];
+ b[1] = input[1];
+ b[2] = input[2];
+ b[3] = input[3];
+#endif
+
+ ROUND (0, context->keys, b, b_next);
+ ROUND (1, context->keys, b, b_next);
+ ROUND (2, context->keys, b, b_next);
+ ROUND (3, context->keys, b, b_next);
+ ROUND (4, context->keys, b, b_next);
+ ROUND (5, context->keys, b, b_next);
+ ROUND (6, context->keys, b, b_next);
+ ROUND (7, context->keys, b, b_next);
+ ROUND (0, context->keys, b, b_next);
+ ROUND (1, context->keys, b, b_next);
+ ROUND (2, context->keys, b, b_next);
+ ROUND (3, context->keys, b, b_next);
+ ROUND (4, context->keys, b, b_next);
+ ROUND (5, context->keys, b, b_next);
+ ROUND (6, context->keys, b, b_next);
+ ROUND (7, context->keys, b, b_next);
+ ROUND (0, context->keys, b, b_next);
+ ROUND (1, context->keys, b, b_next);
+ ROUND (2, context->keys, b, b_next);
+ ROUND (3, context->keys, b, b_next);
+ ROUND (4, context->keys, b, b_next);
+ ROUND (5, context->keys, b, b_next);
+ ROUND (6, context->keys, b, b_next);
+ ROUND (7, context->keys, b, b_next);
+ ROUND (0, context->keys, b, b_next);
+ ROUND (1, context->keys, b, b_next);
+ ROUND (2, context->keys, b, b_next);
+ ROUND (3, context->keys, b, b_next);
+ ROUND (4, context->keys, b, b_next);
+ ROUND (5, context->keys, b, b_next);
+ ROUND (6, context->keys, b, b_next);
+
+ ROUND_LAST (7, context->keys, b, b_next);
+
+#ifdef WORDS_BIGENDIAN
+ output[0] = byte_swap_32 (b_next[0]);
+ output[1] = byte_swap_32 (b_next[1]);
+ output[2] = byte_swap_32 (b_next[2]);
+ output[3] = byte_swap_32 (b_next[3]);
+#else
+ output[0] = b_next[0];
+ output[1] = b_next[1];
+ output[2] = b_next[2];
+ output[3] = b_next[3];
+#endif
+}
+
+
+#ifdef TEST /* We only use CFB mode, thus tehre is no actual need for
+ the decryption fucntion. */
+static void
+serpent_decrypt_internal (serpent_context_t *context,
+ const serpent_block_t input, serpent_block_t output)
+{
+ serpent_block_t b, b_next;
+ int round = ROUNDS;
+
+#ifdef WORDS_BIGENDIAN
+ b_next[0] = byte_swap_32 (input[0]);
+ b_next[1] = byte_swap_32 (input[1]);
+ b_next[2] = byte_swap_32 (input[2]);
+ b_next[3] = byte_swap_32 (input[3]);
+#else
+ b_next[0] = input[0];
+ b_next[1] = input[1];
+ b_next[2] = input[2];
+ b_next[3] = input[3];
+#endif
+
+ ROUND_FIRST_INVERSE (7, context->keys, b_next, b);
+
+ ROUND_INVERSE (6, context->keys, b, b_next);
+ ROUND_INVERSE (5, context->keys, b, b_next);
+ ROUND_INVERSE (4, context->keys, b, b_next);
+ ROUND_INVERSE (3, context->keys, b, b_next);
+ ROUND_INVERSE (2, context->keys, b, b_next);
+ ROUND_INVERSE (1, context->keys, b, b_next);
+ ROUND_INVERSE (0, context->keys, b, b_next);
+ ROUND_INVERSE (7, context->keys, b, b_next);
+ ROUND_INVERSE (6, context->keys, b, b_next);
+ ROUND_INVERSE (5, context->keys, b, b_next);
+ ROUND_INVERSE (4, context->keys, b, b_next);
+ ROUND_INVERSE (3, context->keys, b, b_next);
+ ROUND_INVERSE (2, context->keys, b, b_next);
+ ROUND_INVERSE (1, context->keys, b, b_next);
+ ROUND_INVERSE (0, context->keys, b, b_next);
+ ROUND_INVERSE (7, context->keys, b, b_next);
+ ROUND_INVERSE (6, context->keys, b, b_next);
+ ROUND_INVERSE (5, context->keys, b, b_next);
+ ROUND_INVERSE (4, context->keys, b, b_next);
+ ROUND_INVERSE (3, context->keys, b, b_next);
+ ROUND_INVERSE (2, context->keys, b, b_next);
+ ROUND_INVERSE (1, context->keys, b, b_next);
+ ROUND_INVERSE (0, context->keys, b, b_next);
+ ROUND_INVERSE (7, context->keys, b, b_next);
+ ROUND_INVERSE (6, context->keys, b, b_next);
+ ROUND_INVERSE (5, context->keys, b, b_next);
+ ROUND_INVERSE (4, context->keys, b, b_next);
+ ROUND_INVERSE (3, context->keys, b, b_next);
+ ROUND_INVERSE (2, context->keys, b, b_next);
+ ROUND_INVERSE (1, context->keys, b, b_next);
+ ROUND_INVERSE (0, context->keys, b, b_next);
+
+
+#ifdef WORDS_BIGENDIAN
+ output[0] = byte_swap_32 (b_next[0]);
+ output[1] = byte_swap_32 (b_next[1]);
+ output[2] = byte_swap_32 (b_next[2]);
+ output[3] = byte_swap_32 (b_next[3]);
+#else
+ output[0] = b_next[0];
+ output[1] = b_next[1];
+ output[2] = b_next[2];
+ output[3] = b_next[3];
+#endif
+}
+#endif /*TEST*/
+
+
+static const char *
+serpent_test (void)
+{
+ serpent_context_t context;
+ unsigned char scratch[16];
+ unsigned int i;
+
+ static struct test
+ {
+ int key_length;
+ unsigned char key[32];
+ unsigned char text_plain[16];
+ unsigned char text_cipher[16];
+ } test_data[] =
+ {
+ {
+ 16,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xD2\x9D\x57\x6F\xCE\xA3\xA3\xA7\xED\x90\x99\xF2\x92\x73\xD7\x8E",
+ "\xB2\x28\x8B\x96\x8A\xE8\xB0\x86\x48\xD1\xCE\x96\x06\xFD\x99\x2D"
+ },
+ {
+ 0
+ }
+ };
+
+ for (i = 0; test_data[i].key_length; i++)
+ {
+ serpent_setkey_internal (&context, test_data[i].key,
+ test_data[i].key_length);
+ serpent_encrypt_internal (&context,
+ (const uint32_t *) test_data[i].text_plain,
+ (uint32_t *) scratch);
+
+ if (memcmp (scratch, test_data[i].text_cipher, sizeof (serpent_block_t)))
+ return "Serpent test encryption failed";
+
+#ifdef TEST
+ serpent_decrypt_internal (&context,
+ (const uint32_t *) test_data[i].text_cipher,
+ (uint32_t *) scratch);
+ if (memcmp (scratch, test_data[i].text_plain, sizeof (serpent_block_t)))
+ return "Serpent test decryption failed.";
+#endif /*TEST*/
+ }
+
+ return NULL;
+}
+
+/* Open a new cipher instance with KEY and IV. On error the fucntion
+ returns NULL, otherwise a handle which needs to be provided to the
+ other functions. */
+symenc_t
+symenc_open (const void *key, size_t keylen, const void *iv, size_t ivlen)
+{
+ symenc_t ctx;
+
+ if (!key || keylen != 16 || !iv || ivlen != BLOCKSIZE)
+ return NULL;
+ ctx = calloc (1, sizeof *ctx);
+ if (!ctx)
+ return NULL;
+ if (serpent_setkey (ctx, key, keylen))
+ {
+ free (ctx);
+ return NULL;
+ }
+ memcpy (ctx->u.cfb.iv, iv, BLOCKSIZE);
+ return ctx;
+}
+
+/* Close a Serpent encryption context. */
+void
+symenc_close (symenc_t ctx)
+{
+ if (ctx)
+ {
+ free (ctx);
+ }
+}
+
+
+void
+symenc_cfb_encrypt (symenc_t ctx,
+ void *buffer_out, const void *buffer_in, size_t nbytes)
+{
+ const unsigned char *inbuf = buffer_in;
+ unsigned char *outbuf = buffer_out;
+ unsigned char *ivp;
+ int i;
+
+ assert (ctx->u.cfb.unused <= BLOCKSIZE);
+
+ /* If the input is short enough to be entirely encoded by the
+ remaining XOR mask, XOR the input with the IV and store input
+ into IV. */
+ if (nbytes <= ctx->u.cfb.unused )
+ {
+ for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+ nbytes; nbytes--, ctx->u.cfb.unused--)
+ *outbuf++ = (*ivp++ ^= *inbuf++);
+ return; /* Ready. */
+ }
+
+ /* If there are still some bytes left in the XOR mask, XOR the input
+ with the IV and store input into IV. */
+ if (ctx->u.cfb.unused)
+ {
+ nbytes -= ctx->u.cfb.unused;
+ for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+ ctx->u.cfb.unused; ctx->u.cfb.unused--)
+ *outbuf++ = (*ivp++ ^= *inbuf++);
+ }
+
+ /* Now process complete blocks. */
+ while (nbytes >= BLOCKSIZE)
+ {
+ serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv,
+ (uint32_t*)ctx->u.cfb.iv);
+ for (ivp = ctx->u.cfb.iv, i=0; i < BLOCKSIZE; i++ )
+ *outbuf++ = (*ivp++ ^= *inbuf++);
+ nbytes -= BLOCKSIZE;
+ }
+
+ /* Process any remaining bytes from the last block. */
+ if (nbytes)
+ {
+ serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv,
+ (uint32_t*)ctx->u.cfb.iv);
+ ctx->u.cfb.unused = BLOCKSIZE - nbytes;
+ for (ivp = ctx->u.cfb.iv; nbytes; nbytes--)
+ *outbuf++ = (*ivp++ ^= *inbuf++);
+ }
+}
+
+
+void
+symenc_cfb_decrypt (symenc_t ctx,
+ void *buffer_out, const void *buffer_in, size_t nbytes)
+{
+ const unsigned char *inbuf = buffer_in;
+ unsigned char *outbuf = buffer_out;
+ unsigned char *ivp;
+ uint32_t temp;
+ int i;
+
+ assert (ctx->u.cfb.unused <= BLOCKSIZE);
+
+ /* If the input is short enough to be entirely decoded by the
+ remaining XOR mask, XOR the input with the IV and store input
+ into IV. */
+ if (nbytes <= ctx->u.cfb.unused)
+ {
+ for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+ nbytes; nbytes--, ctx->u.cfb.unused--)
+ {
+ temp = *inbuf++;
+ *outbuf++ = (*ivp ^ temp);
+ *ivp++ = temp;
+ }
+ return; /* Ready. */
+ }
+
+ /* If there are still some bytes left in the XOR mask, XOR the input
+ with the IV and store input into IV. */
+ if (ctx->u.cfb.unused)
+ {
+ nbytes -= ctx->u.cfb.unused;
+ for (ivp = ctx->u.cfb.iv + BLOCKSIZE - ctx->u.cfb.unused;
+ ctx->u.cfb.unused; ctx->u.cfb.unused--)
+ {
+ temp = *inbuf++;
+ *outbuf++ = (*ivp ^ temp);
+ *ivp++ = temp;
+ }
+ }
+
+ /* Now process complete blocks. */
+ while (nbytes >= BLOCKSIZE)
+ {
+ serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv,
+ (uint32_t*)ctx->u.cfb.iv);
+ for (ivp = ctx->u.cfb.iv, i=0; i < BLOCKSIZE; i++)
+ {
+ temp = *inbuf++;
+ *outbuf++ = (*ivp ^ temp);
+ *ivp++ = temp;
+ }
+ nbytes -= BLOCKSIZE;
+ }
+
+ /* Process any remaining bytes from the last block. */
+ if (nbytes)
+ {
+ serpent_encrypt_internal (ctx, (const uint32_t*)ctx->u.cfb.iv,
+ (uint32_t*)ctx->u.cfb.iv);
+ ctx->u.cfb.unused = BLOCKSIZE - nbytes;
+ for (ivp = ctx->u.cfb.iv; nbytes; nbytes--)
+ {
+ temp = *inbuf++;
+ *outbuf++ = (*ivp ^ temp);
+ *ivp++ = temp;
+ }
+ }
+}
+
+
+
+#ifdef TEST
+int
+main (int argc, char** argv)
+{
+ int decmode;
+ symenc_t ctx;
+ char buf[255];
+ size_t n;
+
+ if (argc) { argc--, argv++; }
+
+ decmode = (argc && !strcmp (*argv, "--decrypt"));
+
+ ctx = symenc_open ("1234567890123456", 16, "abcdefghijklmnop", 16);
+ if (!ctx)
+ abort ();
+
+ while ( (n=fread (buf, 1, 255, stdin)) )
+ {
+ if (decmode)
+ symenc_cfb_decrypt (ctx, buf, buf, n);
+ else
+ symenc_cfb_encrypt (ctx, buf, buf, n);
+ if (fwrite (buf, 1, n, stdout) != n)
+ abort ();
+ }
+
+ symenc_close (ctx);
+ return 0;
+}
+
+#endif /*TEST*/
+
+
+
+/*
+Local Variables:
+compile-command: "gcc -Wall -g -O2 -DTEST -o serpent serpent.c"
+End:
+*/
Added: trunk/src/serpent.h
===================================================================
--- trunk/src/serpent.h 2007-08-06 10:48:33 UTC (rev 169)
+++ trunk/src/serpent.h 2007-08-10 07:17:28 UTC (rev 170)
@@ -0,0 +1,55 @@
+/* serpent.h - Definitions of the Serpent encryption algorithm.
+ * Copyright (C) 2007 g10 Code GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL 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 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef SERPENT_H
+#define SERPENT_H
+#ifdef __cplusplus
+extern "C" {
+#if 0
+}
+#endif
+#endif
+
+
+/* Note that there is no special rason that we use Serpent and not AES
+ or even CAST5, blowfish or whatever. Any decent block cipher will
+ do; even the blocksize does not matter for our purpose. The
+ Serpent implementation was just the most convenient one top put
+ into this project. */
+
+struct symenc_context_s;
+typedef struct symenc_context_s *symenc_t;
+
+
+symenc_t symenc_open (const void *key, size_t keylen,
+ const void *iv, size_t ivlen);
+void symenc_close (symenc_t ctx);
+void symenc_cfb_encrypt (symenc_t ctx, void *buffer_out,
+ const void *buffer_in, size_t nbytes);
+void symenc_cfb_decrypt (symenc_t ctx, void *buffer_out,
+ const void *buffer_in, size_t nbytes);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*SERPENT_H*/
More information about the Gnupg-commits
mailing list