/*
 * Blowfish mechanism for udp tunnel
 *
 * mechanisms: blowfish-cbc, blowfish-cbcmac
 * arguments: key size in bits (must be multiple of 8)
 *
 * key values: 8 byte random IV and n byte random key
 *
 * restrictions: plaintext length must be multiple of block size (8 bytes)
 * encoding:         do CBC encryption overwriting message
 * encoding for MAC: do CBC and prepend last ciphertext block
 */
/*
 * This file is part of ipif, part of userv-utils
 *
 * Copyright 1996-2013 Ian Jackson <ijackson@chiark.greenend.org.uk>
 * Copyright 1998 David Damerell <damerell@chiark.greenend.org.uk>
 * Copyright 1999,2003
 *    Chancellor Masters and Scholars of the University of Cambridge
 * Copyright 2010 Tony Finch <fanf@dotat.at>
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with userv-utils; if not, see http://www.gnu.org/licenses/.
 */

#include "forwarder.h"
#include "blowfish.h"

struct mechdata {
  unsigned char iv[BLOWFISH_BLOCKBYTES];
  struct blowfish_cbc_state cbc;
};

static void mds_blowfish(struct mechdata **md_r) {
  struct mechdata *md;
  unsigned long keysize;
  unsigned char key[BLOWFISH_MAXKEYBYTES];

  XMALLOC(md);

  keysize= getarg_ulong();
  arg_assert(!(keysize & 7));
  keysize >>= 3;
  arg_assert(keysize > 0 && keysize <= BLOWFISH_MAXKEYBYTES);

  random_key(md->iv,sizeof(md->iv));
  random_key(key,keysize);

  blowfish_loadkey(&md->cbc.ek, key,keysize);
  *md_r= md;
}

static void mes_blowfish(struct mechdata **md_r, int *maxprefix_io, int *maxsuffix_io) {
  mds_blowfish(md_r);
}

static void mds_bfmac(struct mechdata **md_r) {
  mds_blowfish(md_r);
}

static void mes_bfmac(struct mechdata **md_r, int *maxprefix_io, int *maxsuffix_io) {
  mds_blowfish(md_r);
  *maxprefix_io += BLOWFISH_BLOCKBYTES;
}

#define MSGSIZE_OUT 						      \
  msgsize= buf->size;					              \
  arg_assert(!(msgsize & (BLOWFISH_BLOCKBYTES-1)));

#define MSGSIZE_IN 						      \
  msgsize= buf->size;			   		              \
  if (msgsize & (BLOWFISH_BLOCKBYTES-1)) return "not multiple of block size"

#define FOREACH_BLOCK(func,inptr,outptr)			      \
 {                                                                    \
   unsigned char *ptr;						      \
   blowfish_cbc_setiv(&md->cbc, md->iv);                              \
   for (ptr= buf->start;					      \
        ptr < buf->start + msgsize;				      \
	ptr += BLOWFISH_BLOCKBYTES) {                                 \
     func(&md->cbc,inptr,outptr);				      \
   }                                                                  \
 }

static void menc_blowfish(struct mechdata *md, struct buffer *buf) {
  unsigned long msgsize;
  MSGSIZE_OUT;
  FOREACH_BLOCK(blowfish_cbc_encrypt,ptr,ptr);
}

static const char *mdec_blowfish(struct mechdata *md, struct buffer *buf) {
  unsigned long msgsize;
  MSGSIZE_IN;
  FOREACH_BLOCK(blowfish_cbc_decrypt,ptr,ptr);
  return 0;
}

static void menc_bfmac(struct mechdata *md, struct buffer *buf) {
  unsigned long msgsize;
  unsigned char outblock[BLOWFISH_BLOCKBYTES];
  
  MSGSIZE_OUT;
  FOREACH_BLOCK(blowfish_cbc_encrypt,ptr,outblock);
  memcpy(buf_prepend(buf,BLOWFISH_BLOCKBYTES), outblock, BLOWFISH_BLOCKBYTES);
}

static const char *mdec_bfmac(struct mechdata *md, struct buffer *buf) {
  unsigned long msgsize;
  unsigned char outblock[BLOWFISH_BLOCKBYTES];
  unsigned char *checkblock;

  BUF_UNPREPEND(checkblock,buf,BLOWFISH_BLOCKBYTES);
  MSGSIZE_IN;
  FOREACH_BLOCK(blowfish_cbc_encrypt,ptr,outblock);
  if (memcmp(checkblock,outblock,BLOWFISH_BLOCKBYTES)) return "verify failed";
  return 0;
}

const struct mechanism mechlist_blowfish[]= {
  STANDARD_MECHANISM("blowfish-cbcmac", bfmac)
  STANDARD_MECHANISM("blowfish-cbc",    blowfish)
  { 0 }
};
