#include #include #include "arpc.h" #include "rxx.h" #include "sfskeymisc.h" #include "parseopt.h" #include "sfscrypt.h" #include "sfsschnorr.h" sfscrypt_t sfscrypt; static rxx comma (","); static rxx hexrx ("0x[\\da-fA-F]+"); ptr sfscrypt_t::alloc (const sfs_privkey2_clear &pk, u_char o) const { sfsca *c = xttab[pk.type]; if (!c) return NULL; return c->alloc (pk, o); } ptr sfscrypt_t::alloc (const sfs_pubkey2 &pk, u_char o) const { sfsca *c = xttab[pk.type]; if (!c) return NULL; return c->alloc (pk, o); } ptr sfscrypt_t::alloc (sfs_keytype xt, const str &pk, u_char o) const { sfsca *c = xttab[xt]; if (!c) return NULL; return c->alloc (pk, o); } ptr sfscrypt_t::alloc (const str &s, u_char o) const { vec kv; if (split (&kv, comma, s, 2, true) != 2) return NULL; sfsca *c = strtab[kv[0]]; if (!c) return NULL; return c->alloc (kv[1], o); } ptr sfscrypt_t::alloc (sfs_keytype xt, const str &esk, const eksblowfish *eksb, ptr scon, u_char o) const { sfsca *c = xttab[xt]; str sk = sk_decrypt (esk, eksb); ptr ret = NULL; u_int nskt; const sfs_keytype *skt = c->get_private_keytypes (&nskt); if (skt) { for (u_int i = 0; i < nskt; i++) { c = xttab[skt[i]]; if (c && (ret = c->alloc (sk, scon, o))) break; } } else { ret = c->alloc (sk, scon, o); } return ret; } str sk_decrypt (const str &esk, const eksblowfish *eksb) { if (!esk || !esk.len () ) return NULL; if (esk.len () & (eksb ? 7 : 3)) return NULL; wmstr m (esk.len ()); memcpy (m, esk, m.len ()); if (eksb) { cbc64iv iv (*eksb); iv.decipher_bytes (m, m.len ()); } return m; } str sk_encrypt (const str &sk, const eksblowfish *eksb) { wmstr m (sk.len ()); memcpy (m, sk, m.len ()); if (eksb) { cbc64iv iv (*eksb); iv.encipher_bytes (m, m.len ()); } return m; } ptr sfscrypt_t::alloc_priv (const str &raw, u_char o) const { str salt, sk, pk, kn; sfs_keytype xt; ptr ret; if (!parse (raw, &xt, &salt, &sk, &pk, &kn)) return NULL; if (salt && salt.len ()) return NULL; sfsca *c = xttab[xt]; if (!c) return NULL; ret = c->alloc (dearmor64 (sk), NULL, o); if (!ret) return NULL; if (!verify_sk (ret, xt, pk)) return NULL; return ret; } bool sfscrypt_t::verify_sk (ref k, sfs_keytype xt, const str &pk) const { if (!pk || !pk.len ()) return false; ptr k2 = alloc (xt, pk); if (!k2 || !(*k == *k2)) { strbuf b; k->export_pubkey (b, false); warn << "Error: Public and private keys do not match!\n"; warn << "(Public key should be " << b << ")\n"; return false; } return true; } ptr sfscrypt_t::gen (sfs_keytype xt, u_int nbits, u_char o) const { sfsca *c = xttab[xt]; if (!c) return NULL; ptr r = c->gen (nbits, o); return r; } ptr sfscrypt_t::alloc (const sfs_pubkey &pk, u_char o) const { sfsca *c = xttab[SFS_RABIN]; if (!c) return NULL; return c->alloc (pk, o); } ptr sfscrypt_t::alloc_from_priv (const str &raw) const { str salt, sk, pk, kn; sfs_keytype xt; if (!parse (raw, &xt, &salt, &sk, &pk, &kn)) return NULL; return alloc (xt, pk, (u_char)0); } ptr sfscrypt_t::alloc (const str &raw, str *kn, str *pwd, u_int *cost, u_char o) const { assert (kn && pwd && cost); str salt, sk, pk; sfs_keytype xt; ptr ret; if (!parse (raw, &xt, &salt, &sk, &pk, kn) || !sk || !sk.len ()) return NULL; sfsca *c = xttab[xt]; if (!c) return NULL; eksblowfish eksb; eksblowfish *eksbp = NULL; sk = dearmor64 (sk); if (salt && salt.len ()) { if (!pw_dearmorsalt (cost, NULL, NULL, salt)) return NULL; bool guess = false; for (int i = 0; i < 3; i++) { if (!*pwd) { *pwd = getpwd ("Passphrase for " << *kn << ": "); guess = true; } else i--; eksbp = &eksb; pw_crypt (*pwd, salt, SALTBITS, eksbp); str dsk = sk_decrypt (sk, eksbp); if (dsk && (ret = c->alloc (dsk, NULL, o))) break; *pwd = NULL; } if (!ret) { warn << "Too many tries.\n"; return NULL; } } else { ret = c->alloc (sk, NULL, o); } if (!ret) return NULL; if (!verify_sk (ret, xt, pk)) { warn << "Secret key corruption encountered.\n"; return NULL; } return ret; } #define A64STR "[A-Za-z0-9+/]+={0,2}" bool sfscrypt_t::parse (const str &raw, sfs_keytype *xt, str *salt, str *ske, str *pk, str *kn) const { static rxx r_v1 ("^SK(\\d+),(\\d+\\$" A64STR "\\$)?," "(" A64STR "),([^,]+),(.*)$", ""); static rxx r_v2 ("^SK(\\d+):(\\d+\\$" A64STR "\\$)?:" "(" A64STR "):(.*?):(.*)$", ""); rxx *r = NULL; if (r_v1.search (raw)) r = &r_v1; else if (r_v2.search (raw)) r = &r_v2; else return false; int id; if (!convertint ((*r)[1], &id)) return false; if (xt) *xt = (sfs_keytype )id; if (salt) { *salt = (*r)[2]; } if (ske) { *ske = (*r)[3]; } if (pk) { *pk = (*r)[4]; } if (kn) { *kn = (*r)[5]; } return true; } ptr sfs_rabin_alloc::alloc (const sfs_pubkey &k, u_char o) const { ptr rk = New refcounted (k); ref ret = New refcounted (rk, o); return ret; } ptr sfs_rabin_alloc::gen (u_int nbits, u_char o) const { str s; if (nbits == 0) nbits = sfs_rsasize; if (!sfs_rabin_pub::check_keysize (nbits, &s)) { warn << s << "\n"; return NULL; } ptr k = New refcounted (rabin_keygen (nbits)); ref ret = New refcounted (k, o); return ret; } ptr sfs_rabin_alloc::alloc (const sfs_privkey2_clear &k, u_char o) const { assert (k.type == ktype); ptr rpk = New refcounted (k.rabin->p, k.rabin->q); if (!rpk) return NULL; ref ret = New refcounted (rpk, o); return ret; } ptr sfs_rabin_alloc::alloc (const sfs_pubkey2 &k, u_char o) const { assert (k.type == ktype) ; ptr rpk = New refcounted (*(k.rabin)); if (!rpk) return NULL; ref ret = New refcounted (rpk, o); return ret; } ptr sfs_rabin_alloc::alloc (const str &pk, u_char o) const { if (!hexrx.match (pk)) { warn << "Malformed Rabin public key given\n"; return NULL; } ptr rpk = New refcounted (bigint (pk.cstr ())); if (!rpk) return NULL; ref ret = New refcounted (rpk, o); return ret; } bool sfscrypt_t::verify (const sfs_pubkey2 &pk, const sfs_sig2 &sig, const str &msg, str *e) const { ptr p = alloc (pk, SFS_VERIFY); if (!p) { str s = "Could not user public key to verify"; if (e) *e = s; else warn << s << "\n"; return false; } bool ret = p->verify (sig, msg, e); return ret; } bool sfspriv::export_keyhalf (sfsauth_keyhalf *kh, bool *dlt) const { *dlt = false; kh->set_type (SFSAUTH_KEYHALF_NONE); return true; } bool sfspub::operator== (const sfspub &p) const { sfs_pubkey2 pk; return (export_pubkey (&pk) && p == pk); } bool sfspub::operator== (const sfs_pubkey2 &p1) const { sfs_pubkey2 p2; return (export_pubkey (&p2) && (xdr2str (p2) == xdr2str (p1))); } bool sfspub::operator== (const str &b) const { strbuf f; return (export_pubkey (f) && (str (f) == b)); } bool sfspub::verify_init (const sfs_sig2 &sig, const str &msg, str *e) const { if (!check_keysize (e)) { return false; } if (sig.type != ktype) { str s = "public key type mismatch\n"; if (e) *e = s; warn << s << "\n"; return false; } return true; } bool sfspriv::export_privkey (str *s, const eksblowfish *eksb) const { str tmp; str2wstr (tmp); if (!export_privkey (&tmp) || !tmp) return false; if (eksb && (tmp.len () & 4)) return false; *s = eksb ? sk_encrypt (tmp, eksb) : tmp; return true; } bool sfspriv::export_privkey (str *s, const str &kn, str pwd, u_int cost) const { str salt = ""; str seckey; if (pwd) { salt = pw_gensalt (cost); if (!salt) return NULL; eksblowfish eksb; pw_crypt (pwd, salt, SALTBITS, &eksb); if (!export_privkey (&seckey, &eksb)) return false; } else if (!export_privkey (&seckey, NULL)) return false; return export_privkey (s, salt, seckey, kn); } bool sfs_rabin_priv::sign (sfs_sig2 *sig, const str &msg) { assert (get_opt (SFS_SIGN)); sig->set_type (SFS_RABIN); if ((*sig->rabin = privk->sign (msg)) != 0) return true; else return false; } bool sfs_rabin_pub::verify (const sfs_sig2 &sig, const str &msg, str *e) const { assert (get_opt (SFS_VERIFY)); if (!verify_init (sig, msg, e)) return false; bool ret = pubk->verify (msg, *(sig.rabin)); if (!ret && e) *e = "signature verification failed"; return ret; } bool sfs_rabin_pub::verify_r (const bigint &n, size_t len, str &msg, str *e) const { assert (get_opt (SFS_VERIFY)); if (!check_keysize (e)) return false; msg = pubk->verify_r (n, len); if (msg) return true; else { if (e) *e = "signature verification failed"; return false; } } bool sfs_rabin_priv::sign_r (sfs_sig *sig, const str &msg) const { assert (get_opt (SFS_SIGN)); if (!(*sig = privk->sign_r (msg))) return false; return true; } bool sfs_rabin_priv::export_privkey (sfs_privkey2_clear *k) const { k->set_type (ktype); k->rabin->p = privk->p; k->rabin->q = privk->q; return true; } bool sfs_rabin_pub::export_pubkey (sfs_pubkey2 *k) const { k->set_type (ktype); *k->rabin = pubk->n; return true; } bool sfs_rabin_pub::check_keysize (size_t nbits, str *s) { strbuf b; if (nbits > sfs_maxrsasize) { b << "Rabin key is " << nbits << " bits but maximum allowed size is " << sfs_maxrsasize << " bits"; } else if (nbits < sfs_minrsasize) { b << "Rabin key is " << nbits << " bits but minimum allowed size is " << sfs_minrsasize << " bits"; } else { return true; } if (s) *s = b; return false; } bool sfs_rabin_pub::check_keysize (str *s) const { return check_keysize (pubk->n.nbits (), s); } bool sfs_rabin_pub::export_pubkey (strbuf &b, bool prefix) const { if (prefix) b << keylabel << "," ; b << "0x" << pubk->n.getstr (16); return true; } bool sfs_rabin_pub::encrypt (sfs_ctext2 *ct, const str &msg) const { assert (get_opt (SFS_ENCRYPT)); ct->set_type (ktype); if (!(*(ct->rabin) = pubk->encrypt (msg))) return false; return true; } bool sfs_rabin_priv::decrypt (const sfs_ctext &ct, str *msg) const { assert (get_opt (SFS_DECRYPT)); if (!ct) return false; *msg = privk->decrypt (ct, sizeof (sfs_kmsg)); if (!msg || msg->len () < 0) return false; return true; } bool sfs_rabin_priv::decrypt (const sfs_ctext2 &ct, str *msg) const { if (ct.type != ktype) return false; if (!ct.rabin) return false; *msg = privk->decrypt (*ct.rabin, sizeof (sfs_kmsg)); if (!msg || msg->len () < 0) return false; return true; } bool sfs_rabin_pub::encrypt (sfs_ctext *n, const str &msg) const { assert (get_opt (SFS_ENCRYPT)); if (!(*n = pubk->encrypt (msg))) return false; return true; } bool sfs_rabin_pub::export_pubkey (sfs_pubkey *k) const { if (!(*k = pubk->n)) return false; return true; } bool sfs_rabin_priv::get_privkey_hash (u_int8_t *buf, const sfs_hash &hostid) const { str p; sha1ctx sc; p = str2wstr (privk->p.getraw ()); sc.update (p, p.len ()); sc.update (hostid.base (), hostid.size ()); p = str2wstr (privk->q.getraw ()); sc.update (p, p.len ()); sc.final (buf); return true; } bool sfs_rabin_priv::export_privkey (str *s) const { sfs_rabin_priv_export rexp; xdrsuio x; if (!xdr_putint (&x, SFS_RABIN) || !xdr_putbigint (&x, privk->n)) return false; sha1_hashv (&rexp.cksum, x.iov (), x.iovcnt ()); rexp.p = privk->p; rexp.q = privk->q; if ((*s = xdr2str_pad (rexp, true, 8))) return true; return false; } bool sfspriv::export_privkey (str *s, const str &salt, const str &sk, const str &kn) const { strbuf b ("SK%d", (int )ktype); b << ":" << salt << ":" << armor64 (sk) << ":" ; export_pubkey (b, false); b << ":" << kn; *s = b; return true; } void sfspriv::signcb (str *errp, sfs_sig2 *sigp, str err, ptr sig) { if (err) *errp = err; else *errp = ""; if (sig) *sigp = *sig; } void sfspriv::sign (const sfsauth2_sigreq &sr, sfs_authinfo ainfo, cbsign cb) { ptr sig = New refcounted (); str msg = sigreq2str (sr); if (!msg) { (*cb) ("Could not convert sfs_updatereq to string", NULL); return; } bool rc = sign (sig, msg); if (!rc) (*cb) ("Synchronous sign failed", NULL); else (*cb) (NULL, sig); } ptr sfs_rabin_alloc::alloc (const str &raw, ptr dummy, u_char o) const { u_char h[sha1::hashsize]; sfs_rabin_priv_export rexp; if (!str2xdr (rexp, raw) || rexp.p >= rexp.q || rexp.p <= 1 || rexp.q <= 1) return NULL; ref rsk = New refcounted (rexp.p, rexp.q); xdrsuio x; if (!xdr_putint (&x, SFS_RABIN) || !xdr_putbigint (&x, rsk->n)) return NULL; sha1_hashv (h, x.iov (), x.iovcnt ()); if (memcmp (h, &rexp.cksum, sizeof (h))) return NULL; ref ret = New refcounted (rsk, o); return ret; } sfscrypt_t::sfscrypt_t () { add (New sfs_rabin_alloc ()); add (New sfs_schnorr_alloc ()); add (New sfs_1schnorr_alloc ()); add (New sfs_2schnorr_alloc ()); } str timestr () { char buf[80]; struct timeval t; struct tm *tmp; if (gettimeofday (&t, NULL) < 0 || !(tmp = localtime ((time_t *)&t.tv_sec))) return "** TIME LOOKUP FAILED **"; int n = strftime (buf, sizeof (buf), "%a, %d %b %Y %H:%M:%S %z", tmp); assert (implicit_cast (n) < sizeof (buf)); return buf; } bool sfspub::get_pubkey_hash (sfs_hash *h) const { sfs_pubkey2 p; if (!export_pubkey (&p)) return false; sha1_hashxdr (h->base (), p); return true; } str sfspub::get_pubkey_hash () const { sfs_hash h; if (!get_pubkey_hash (&h)) return NULL; return str (h.base (), h.size ()); } void sfspriv::init (cbs cb) { sfs_authinfo ainfo; ainfo.type = SFS_NULL; sfsauth2_sigreq sr (SFS_NULL); null_sigreq (&sr); sign (sr, ainfo, wrap (this, &sfspriv::initcb, cb)); } void sfspriv::initcb (cbs cb, str err, ptr sig) { if (!sig && !err) err = "no valid signature returned from server."; (*cb) (err); }