+/* Forward declaration: parse_part_sign_sha256 and parse_part_encr_aes256 call
+ * parse_packet and vice versa. */
+#define PP_SIGNED 0x01
+#define PP_ENCRYPTED 0x02
+static int parse_packet (sockent_t *se,
+ void *buffer, size_t buffer_size, int flags);
+
+#if HAVE_GCRYPT_H
+static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_len)
+{
+ char *buffer = *ret_buffer;
+ size_t buffer_len = *ret_buffer_len;
+
+ part_signature_sha256_t ps_received;
+ char hash[sizeof (ps_received.hash)];
+
+ gcry_md_hd_t hd;
+ gcry_error_t err;
+ unsigned char *hash_ptr;
+
+ if (se->shared_secret == NULL)
+ {
+ NOTICE ("network plugin: Received signed network packet but can't verify "
+ "it because no shared secret has been configured. Will accept it.");
+ return (0);
+ }
+
+ if (buffer_len < sizeof (ps_received))
+ return (-ENOMEM);
+
+ hd = NULL;
+ err = gcry_md_open (&hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ if (err != 0)
+ {
+ ERROR ("network plugin: Creating HMAC-SHA-256 object failed: %s",
+ gcry_strerror (err));
+ return (-1);
+ }
+
+ err = gcry_md_setkey (hd, se->shared_secret,
+ strlen (se->shared_secret));
+ if (err != 0)
+ {
+ ERROR ("network plugin: gcry_md_setkey failed: %s",
+ gcry_strerror (err));
+ gcry_md_close (hd);
+ return (-1);
+ }
+
+ memcpy (&ps_received, buffer, sizeof (ps_received));
+ /* TODO: Check ps_received.head.length! */
+
+ buffer += sizeof (ps_received);
+ buffer_len -= sizeof (ps_received);
+
+ gcry_md_write (hd, buffer, buffer_len);
+ hash_ptr = gcry_md_read (hd, GCRY_MD_SHA256);
+ if (hash_ptr == NULL)
+ {
+ ERROR ("network plugin: gcry_md_read failed.");
+ gcry_md_close (hd);
+ return (-1);
+ }
+ memcpy (hash, hash_ptr, sizeof (hash));
+
+ gcry_md_close (hd);
+ hd = NULL;
+
+ *ret_buffer += sizeof (ps_received);
+ *ret_buffer_len -= sizeof (ps_received);
+
+ if (memcmp (ps_received.hash, hash,
+ sizeof (ps_received.hash)) == 0)
+ return (0);
+ else /* hashes do not match. */
+ return (1);
+} /* }}} int parse_part_sign_sha256 */
+/* #endif HAVE_GCRYPT_H */
+
+#else /* if !HAVE_GCRYPT_H */
+static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_len)
+{
+ INFO ("network plugin: Received signed packet, but the network "
+ "plugin was not linked with libgcrypt, so I cannot "
+ "verify the signature. The packet will be accepted.");
+ return (0);
+} /* }}} int parse_part_sign_sha256 */
+#endif /* !HAVE_GCRYPT_H */
+
+#if HAVE_GCRYPT_H
+static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_len,
+ int flags)
+{
+ char *buffer = *ret_buffer;
+ size_t buffer_len = *ret_buffer_len;
+ size_t orig_buffer_len;
+ size_t part_size;
+ size_t buffer_offset;
+ size_t padding_size;
+ part_encryption_aes256_t pea;
+ unsigned char hash[sizeof (pea.hash)];
+
+ gcry_cipher_hd_t cypher;
+ gcry_error_t err;
+
+ /* Make sure at least the header if available. */
+ if (buffer_len < sizeof (pea))
+ {
+ NOTICE ("network plugin: parse_part_encr_aes256: "
+ "Discarding short packet.");
+ return (-1);
+ }
+
+ buffer_offset = 0;
+
+#define BUFFER_READ(p,s) do { \
+ memcpy ((p), buffer + buffer_offset, (s)); \
+ buffer_offset += (s); \
+} while (0)
+
+ /* Copy the unencrypted information into `pea'. */
+ BUFFER_READ (&pea.head.type, sizeof (pea.head.type));
+ BUFFER_READ (&pea.head.length, sizeof (pea.head.length));
+ BUFFER_READ (pea.iv, sizeof (pea.iv));
+
+ /* Check the `part size'. */
+ part_size = ntohs (pea.head.length);
+ if (part_size > buffer_len)
+ {
+ NOTICE ("network plugin: parse_part_encr_aes256: "
+ "Discarding large part.");
+ return (-1);
+ }
+
+ cypher = network_get_aes256_cypher (se, pea.iv, sizeof (pea.iv));
+ if (cypher == NULL)
+ return (-1);
+
+ /* Decrypt the packet in-place */
+ err = gcry_cipher_decrypt (cypher,
+ buffer + PART_ENCRYPTION_AES256_UNENCR_SIZE,
+ part_size - PART_ENCRYPTION_AES256_UNENCR_SIZE,
+ /* in = */ NULL, /* in len = */ 0);
+ if (err != 0)
+ {
+ ERROR ("network plugin: gcry_cipher_decrypt returned: %s",
+ gcry_strerror (err));
+ return (-1);
+ }
+
+ /* Figure out the length of the payload and the length of the padding. */
+ BUFFER_READ (&pea.orig_length, sizeof (pea.orig_length));
+
+ orig_buffer_len = ntohs (pea.orig_length);
+ if (orig_buffer_len > (part_size - PART_ENCRYPTION_AES256_SIZE))
+ {
+ ERROR ("network plugin: Decryption failed: Invalid original length.");
+ return (-1);
+ }
+
+ /* Calculate the size of the `padding' field. */
+ padding_size = part_size - (orig_buffer_len + PART_ENCRYPTION_AES256_SIZE);
+ if (padding_size > sizeof (pea.padding))
+ {
+ ERROR ("network plugin: Part- and original length "
+ "differ more than %zu bytes.", sizeof (pea.padding));
+ return (-1);
+ }
+
+ BUFFER_READ (pea.hash, sizeof (pea.hash));
+
+ /* Read the padding. */
+ BUFFER_READ (pea.padding, padding_size);
+
+ /* Check hash sum */
+ memset (hash, 0, sizeof (hash));
+ gcry_md_hash_buffer (GCRY_MD_SHA1, hash,
+ buffer + buffer_offset, orig_buffer_len);
+
+ if (memcmp (hash, pea.hash, sizeof (hash)) != 0)
+ {
+ ERROR ("network plugin: Decryption failed: Checksum mismatch.");
+ return (-1);
+ }
+
+ assert ((PART_ENCRYPTION_AES256_SIZE + padding_size + orig_buffer_len)
+ == part_size);
+
+ parse_packet (se, buffer + PART_ENCRYPTION_AES256_SIZE + padding_size,
+ orig_buffer_len, flags | PP_ENCRYPTED);
+
+ /* Update return values */
+ *ret_buffer = buffer + part_size;
+ *ret_buffer_len = buffer_len - part_size;
+
+ return (0);
+#undef BUFFER_READ
+} /* }}} int parse_part_encr_aes256 */
+/* #endif HAVE_GCRYPT_H */
+
+#else /* if !HAVE_GCRYPT_H */
+static int parse_part_encr_aes256 (sockent_t *se, /* {{{ */
+ void **ret_buffer, size_t *ret_buffer_len, int flags)
+{
+ INFO ("network plugin: Received encrypted packet, but the network "
+ "plugin was not linked with libgcrypt, so I cannot "
+ "decrypt it. The packet will be discarded.");
+ return (-1);
+} /* }}} int parse_part_encr_aes256 */
+#endif /* !HAVE_GCRYPT_H */
+
+static int parse_packet (sockent_t *se, /* {{{ */
+ void *buffer, size_t buffer_size, int flags)