/* $Id: sfscrypt.h,v 1.17 2002/10/23 22:01:54 max Exp $ */
/*
 *
 * Copyright (C) 2002 David Mazieres 
 *
 * This program 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 2, 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

// SFSCRYPT
//
// This library servers as an abstract interface between specific
// cryptographic implementations and the generic XDR implementations
// of keys, ciphertexts, and signatures.
//

// XXX - a lot of functions should be inlined.... if we can get the compiler
// to agree with that..

#ifndef _SFSMISC_SFSCRYPT_H
#define _SFSMISC_SFSCRYPT_H 1

#include "crypt.h"
#include "rabin.h"
#include "serial.h"
#include "sfs_prot.h"
#include "sfsauth_prot.h"
#include "sfsagent.h"
#include "sfsconnect.h"
#include "sfsmisc.h"

#define SFS_ENCRYPT   (1 << 0)
#define SFS_VERIFY    (1 << 1)
#define SFS_DECRYPT   (1 << 2)
#define SFS_SIGN      (1 << 3)


class sfspub {
public:
  sfspub (sfs_keytype kt, u_char o = 0, const str &k = NULL) 
    : keylabel (k), ktype (kt), opts (o) {}
  virtual ~sfspub () {}
  sfs_keytype get_type () const { return ktype; }

  // Should be implemented by child classes
  virtual bool export_pubkey (sfs_pubkey2 *k) const = 0;
  virtual bool export_pubkey (strbuf &sb, bool prefix = true) const = 0;
  virtual bool check_keysize (str *s = NULL) const = 0;
  virtual bool encrypt (sfs_ctext2 *ct, const str &msg) const { return false; }
  virtual bool verify (const sfs_sig2 &sig, const str &msg, str *e = NULL) 
    const { return false; }

  // These have reasonable default behavior
  virtual bool operator== (const str &s) const;
  virtual bool operator== (const sfspub &p) const;
  virtual bool operator== (const sfs_pubkey2 &pk) const;
  virtual bool operator== (const sfsauth_keyhalf &kh) const { return false; }
  virtual void set_username (const str &s) {} 
  virtual void set_hostname (const str &s) {}
  virtual str  get_hostname () const { return NULL; }
  virtual bool is_proac () const { return false; }
  
  // Legacy Code -- To be Phased out
  virtual bool is_v2 () const { return true; }
  virtual bool export_pubkey (sfs_pubkey *k) const { return false; }
  virtual bool encrypt (sfs_ctext *ct, const str &msg) const { return false; }
  virtual bool verify_r (const bigint &n, size_t len, str &msg, str *e = NULL) 
    const { return false; }

  bool check_opts () const { return (!get_opt (get_bad_opts ())); }
  bool get_pubkey_hash (sfs_hash *h) const ;
  str get_pubkey_hash () const;

  const str keylabel;
protected:
  bool verify_init (const sfs_sig2 &sig, const str &msg, str *e) const;
  virtual u_char get_bad_opts () const { return (SFS_DECRYPT | SFS_SIGN); }
  bool get_opt (u_char o) const { return (opts & o); }
  const sfs_keytype ktype;
  const u_char opts;
};

class sfspriv : virtual public sfspub {
public:
  sfspriv (sfs_keytype kt, u_char o = 0, const str &k = NULL) 
    : sfspub (kt, o, k) {}
  virtual ~sfspriv () {}

  // The Following 4 functions should be implemented by child classes
  virtual bool export_privkey (str *s) const = 0;
  virtual bool get_privkey_hash (u_int8_t *buf, const sfs_hash &hostid) 
    const = 0;
  virtual bool decrypt (const sfs_ctext2 &ct, str *msg) const = 0;
  virtual bool sign (sfs_sig2 *sig, const str &msg) = 0;

  // Asynchronous version ; reasonable default is given
  virtual void sign (const sfsauth2_sigreq &sr, sfs_authinfo ainfo, 
		     cbsign cb);
  
  // Legacy Code -- to be phased out
  virtual bool decrypt (const sfs_ctext &ct, str *msg) const { return false; }
  virtual bool sign_r (sfs_sig *sig, const str &msg) const { return false; }

  // Implemented in terms of export_privkey (str *s)
  virtual bool export_privkey (str *s, const str &kn, str pwd, u_int cost) 
    const;
  virtual bool export_privkey (str *s, const eksblowfish *eksb) const;

  bool export_privkey (str *s, const str &salt, const str &ske,
		       const str &kn) const;
  virtual bool export_keyhalf (sfsauth_keyhalf *, bool *) const ;

  virtual bool export_privkey (sfs_privkey2_clear *k) const { return false; }
  virtual str get_desc () const { return ("generic private key"); }

  // Initialize a key by signing a null request
  void init (cbs cb);
  void initcb (cbs cb, str err, ptr<sfs_sig2> sig);

  // Functions needed for abstraction of multi-party signatures
  virtual ptr<sfspriv> regen () const { return NULL; }
  virtual ptr<sfspriv> update () const { return NULL; }
  virtual ptr<sfspriv> wholekey () const { return NULL; }
  virtual bool get_coninfo (ptr<sfscon> *c, str *h) const { return false; }

protected:
  u_char get_bad_modes () { return 0; }
  void signcb (str *errp, sfs_sig2 *sigp, str err, ptr<sfs_sig2> sig);
};

class sfsca { // SFS Crypt Allocator
public:
  sfsca () : ktype (SFS_NOKEY) {}
  sfsca (sfs_keytype x, const str &s) : ktype (x), skey (s) {} 

  // These 6 functions should be implemented by child classes
  virtual ptr<sfspub>  alloc (const sfs_pubkey2 &pk, u_char o = 0) const = 0;
  virtual ptr<sfspub>  alloc (const str &s, u_char o = 0) const = 0;
  virtual ptr<sfspriv> alloc (const sfs_privkey2_clear &pk, u_char o = 0) 
    const = 0;
  virtual ptr<sfspriv> alloc (const str &raw, ptr<sfscon> s,
			      u_char o = 0) const = 0;
  virtual ptr<sfspriv> gen (u_int nbits, u_char o = 0) const { return NULL; }
  virtual const sfs_keytype *get_private_keytypes (u_int *n) const 
  { return NULL; }

  // Legacy Code
  virtual ptr<sfspub> alloc (const sfs_pubkey &pk, u_char o = 0) const 
  { return NULL; }

  virtual ~sfsca () {}
  
  ihash_entry<sfsca> hlinks;
  ihash_entry<sfsca> hlinkx;
  sfs_keytype ktype;
  str skey;
};

class sfs_rabin_pub : virtual public sfspub {
public:
  sfs_rabin_pub (ref<rabin_pub> k, u_char o = 0) 
    : sfspub (SFS_RABIN, o, "rabin"), pubk (k) {}
  bool verify (const sfs_sig2 &sig, const str &msg, str *e = NULL) const;
  bool export_pubkey (sfs_pubkey2 *k) const;
  bool export_pubkey (strbuf &b, bool prefix = true) const;
  bool check_keysize (str *s = NULL) const;
  bool encrypt (sfs_ctext2 *ct, const str &msg) const;

  static bool check_keysize (size_t nbits, str *s = NULL);

  // Legacy Code
  bool verify_r (const bigint &n, size_t len, str &msg, str *e = NULL) const;
  bool export_pubkey (sfs_pubkey *k) const;
  bool encrypt (sfs_ctext *ct, const str &msg) const;
  bool is_v2 () const { return false; }
private:
  ref<rabin_pub> pubk;
};

class sfs_rabin_priv : public sfs_rabin_pub, public sfspriv {
public:
  sfs_rabin_priv (ref<rabin_priv> k, u_char o = 0)
    : sfspub (SFS_RABIN, o, "rabin"), sfs_rabin_pub (k), 
      sfspriv (SFS_RABIN, o, "rabin"), privk (k) {}

  bool sign (sfs_sig2 *s, const str &msg);
  bool export_privkey (sfs_privkey2_clear *k) const;
  bool export_privkey (str *s) const;
  bool decrypt (const sfs_ctext2 &ct, str *msg) const;
  bool get_privkey_hash (u_int8_t *buf, const sfs_hash &hostid) const;

  // Legacy Code
  bool sign_r (sfs_sig *sig, const str &msg) const;
  bool decrypt (const sfs_ctext &ct, str *msg) const;

  str get_desc () const { strbuf b; export_pubkey (b); return b;}

private:
  ref<rabin_priv> privk;
};


class sfs_rabin_alloc : public sfsca {
public:
  sfs_rabin_alloc (sfs_keytype kt = SFS_RABIN, const str &s = "rabin") 
    : sfsca (kt, s) {}

  ptr<sfspriv> alloc (const sfs_privkey2_clear &pk, u_char o = 0) const;
  ptr<sfspub>  alloc (const sfs_pubkey2 &pk, u_char o = 0) const;
  ptr<sfspub>  alloc (const str &s, u_char o = 0) const;
  ptr<sfspriv> gen (u_int nbits, u_char o = 0) const;
  ptr<sfspriv> alloc (const str &raw, ptr<sfscon> c, u_char o = 0) const;

  // Old protocol
  ptr<sfspub> alloc (const sfs_pubkey &pk, u_char o = 0) const;
};

class sfscrypt_t {
public:
  sfscrypt_t ();

  ptr<sfspriv> alloc (const sfs_privkey2_clear &pk, u_char o = 0) const;
  ptr<sfspriv> alloc (const str &ske, str *kn, str *pwd, u_int *cost, 
		      u_char o = 0) const;
  ptr<sfspub>  alloc (const sfs_pubkey2 &pk, u_char o = 0) const;
  ptr<sfspub>  alloc (const str &s, u_char o = 0) const;
  ptr<sfspriv> alloc (sfs_keytype xt, const str &esk, const eksblowfish *eksb, 
		      ptr<sfscon> s = NULL, u_char o = 0)
    const;
  ptr<sfspriv> gen (sfs_keytype xt, u_int nbits, u_char o = 0) const;
  ptr<sfspriv> alloc_priv (const str &sk, u_char o = 0) const;
  ptr<sfspub>  alloc_from_priv (const str &sk) const;

  // Legacy Code
  ptr<sfspub> alloc (const sfs_pubkey &pk, u_char o = 0) const;

  bool verify (const sfs_pubkey2 &pk, const sfs_sig2 &sig, const str &msg,
	       str *e = NULL) const;

  void add (sfsca *c)
  {
    if (c->skey.len ()) { strtab.insert (c); }
    if (c->ktype && !xttab[c->ktype]) { xttab.insert (c); }
  }
private:
  ptr<sfspub>  alloc (sfs_keytype xt, const str &pk, u_char o = 0) const;
  bool parse (const str &raw, sfs_keytype *xt, str *salt, str *ske, str *pk, 
	      str *kn) const;
  bool verify_sk (ref<sfspriv> k, sfs_keytype xt, const str &pk) const ;

  ihash<str, sfsca, &sfsca::skey, &sfsca::hlinks> strtab; 
  ihash<sfs_keytype, sfsca, &sfsca::ktype, &sfsca::hlinkx> xttab; 
};


#define SALTBITS SK_RABIN_SALTBITS

extern sfscrypt_t sfscrypt;

str sk_decrypt (const str &epk, const eksblowfish *eksb);
str sk_encrypt (const str &pk, const eksblowfish *eksb);
str timestr ();

template<class T> str
xdr2str_pad (const T &t, bool scrub = false, u_int align = 8)
{
  xdrsuio x (XDR_ENCODE, scrub);
  XDR *xp = &x;
  if (!rpc_traverse (xp, const_cast<T &> (t)))
    return NULL;
  while (x.uio ()->resid () & (align >> 1)) 
    if (!xdr_putint (&x, 0))
      return NULL;
  mstr m (x.uio ()->resid ());
  x.uio ()->copyout (m);
  if (scrub)
    return str2wstr (m);
  return m;
}


#endif /* _SFSMISC_SFSCRYPT_H */
