package thaw.plugins.signatures; import java.awt.Color; import java.sql.*; import java.util.Vector; import java.util.Iterator; import java.util.List; import frost.crypt.FrostCrypt; import frost.util.XMLTools; import org.w3c.dom.*; import java.io.File; import thaw.core.Logger; import thaw.core.I18n; import thaw.plugins.Hsqldb; import thaw.plugins.Signatures; import thaw.core.Config; public class Identity { public final static int[] trustLevelInt = { 100, 10, 5, 1, 0, -1, -5, -10 }; public final static String[] trustLevelStr = { I18n.getMessage("thaw.plugin.signature.trustLevel.dev"), I18n.getMessage("thaw.plugin.signature.trustLevel.trustworthy"), I18n.getMessage("thaw.plugin.signature.trustLevel.good"), I18n.getMessage("thaw.plugin.signature.trustLevel.observe"), I18n.getMessage("thaw.plugin.signature.trustLevel.check"), I18n.getMessage("thaw.plugin.signature.trustLevel.bad"), I18n.getMessage("thaw.plugin.signature.trustLevel.evil"), I18n.getMessage("thaw.plugin.signature.trustLevel.asshole") }; public final static String[] trustLevelUserStr= { I18n.getMessage("thaw.plugin.signature.trustLevel.trustworthy"), I18n.getMessage("thaw.plugin.signature.trustLevel.good"), I18n.getMessage("thaw.plugin.signature.trustLevel.observe"), I18n.getMessage("thaw.plugin.signature.trustLevel.check"), I18n.getMessage("thaw.plugin.signature.trustLevel.bad"), I18n.getMessage("thaw.plugin.signature.trustLevel.evil"), I18n.getMessage("thaw.plugin.signature.trustLevel.asshole") }; public final static Color[] trustLevelColor = { Color.BLUE, new Color(0, 200, 0), /* light green */ new Color(0, 150, 0), new Color(0, 80, 0), /* green */ Color.BLACK, new Color(125, 0, 0), /* moderatly red */ new Color(200, 0, 0), new Color(255, 0, 0) }; public final static Color trustLevelColorMe = new Color(127, 127, 255) /* weird color */; private Hsqldb db; private int id = -1; private String nick; /* public key (aka Y) */ private String publicKey; /* private key (aka X) */ private String privateKey; private boolean isDup; private int trustLevel; private String hash; private static FrostCrypt frostCrypt; protected Identity() { } /** * If you don't have a value, let it to null and pray it won't be used :P * @param nick part *before* the @ */ public Identity(Hsqldb db, int id, String nick, String publicKey, String privateKey, boolean isDup, int trustLevel) { if (nick == null || publicKey == null) { Logger.error(this, "missing value ?!"); if (nick == null) Logger.error(this, "nick missing"); if (publicKey == null) Logger.error(this, "publicKey missing"); } this.db = db; this.id = id; this.nick = nick; this.publicKey = publicKey; this.privateKey = privateKey; this.isDup = isDup; this.trustLevel = trustLevel; //hash = Base64.encode(SHA256.digest(publicKey.getBytes("UTF-8"))); initFrostCrypt(); hash = frostCrypt.digest(publicKey); } protected void setDb(Hsqldb db) { this.db = db; } public Hsqldb getDb() { return db; } protected void setId(int id) { this.id = id; } private static void initFrostCrypt() { if (frostCrypt == null) frostCrypt = new FrostCrypt(); } public int getId() { return id; } public String getNick() { return nick; } public String getPublicKey() { return publicKey; } public String getPrivateKey() { return privateKey; } public int getTrustLevel() { return trustLevel; } public static int getTrustLevel(String str) { int i; for (i = 0 ; i < trustLevelStr.length ; i++) { if (trustLevelStr[i].equals(str)) return trustLevelInt[i]; } return 0; } public String getTrustLevelStr() { if (privateKey != null) { return I18n.getMessage("thaw.plugin.signature.trustLevel.me"); } return getTrustLevelStr(getTrustLevel()); } public static String getTrustLevelStr(int trustLevel) { int i; for (i = 0 ; i < trustLevelInt.length ; i++) { if (trustLevelInt[i] == trustLevel) return trustLevelStr[i]; } return "[?]"; } public boolean isDup() { if (privateKey != null) return false; return isDup; } public Color getTrustLevelColor() { int i; if (privateKey != null) return trustLevelColorMe; for (i = 0 ; i < trustLevelInt.length ; i++) { if (trustLevelInt[i] == trustLevel) break; } if (i < trustLevelInt.length) { return trustLevelColor[i]; } return Color.BLACK; } public void setTrustLevel(String str) { int i; for (i = 0 ; i < Identity.trustLevelStr.length ; i++) { if (Identity.trustLevelStr[i].equals(str)) break; } if (i >= Identity.trustLevelStr.length) { Logger.error(this, "Unknown trust level: "+str); return; } setTrustLevel(trustLevelInt[i]); } public void setTrustLevel(int i) { try { synchronized(db.dbLock) { PreparedStatement st; st = db.getConnection().prepareStatement("UPDATE signatures SET trustLevel = ? WHERE id = ?"); st.setInt(1, i); st.setInt(2, id); st.execute(); st.close(); } trustLevel = i; Signatures.notifyIdentityUpdated(this); } catch(SQLException e) { Logger.error(this, "Unable to change trust level because: "+e.toString()); e.printStackTrace(); } } /** * will put all the other identities with the same nickname as duplicata, * and will put this identity as non duplicate */ public void setOriginal() { try { synchronized(db.dbLock) { PreparedStatement st; st = db.getConnection().prepareStatement("UPDATE signatures SET isDup = TRUE " + "WHERE LOWER(nickName) = ?"); st.setString(1, nick.toLowerCase()); st.execute(); st.close(); st = db.getConnection().prepareStatement("UPDATE signatures SET isDup = FALSE " + "WHERE id = ?"); st.setInt(1, id); st.execute(); st.close(); } } catch(SQLException e) { Logger.error(this, "SQLException while setting the identity as original : " +e.toString()); e.printStackTrace(); } } public boolean mustBeIgnored(Config config) { if (privateKey != null) return false; int min = Integer.parseInt(config.getValue("minTrustLevel")); return (trustLevel < min); } /** * if the identity doesn't exists, it will be created */ public static Identity getIdentity(Hsqldb db, String nick, String publicKey) { return getIdentity(db, nick, publicKey, true); } public static Identity getIdentity(Hsqldb db, String nick, String publicKey, boolean create) { if (nick == null || publicKey == null) return null; try { synchronized(db.dbLock) { PreparedStatement st; st = db.getConnection().prepareStatement("SELECT id, nickName, publicKey, "+ "privateKey, isDup, trustLevel "+ "FROM signatures "+ "WHERE publicKey = ? LIMIT 1"); st.setString(1, publicKey); ResultSet set = st.executeQuery(); if (set.next()) { Identity i = new Identity(db, set.getInt("id"), set.getString("nickName"), set.getString("publicKey"), set.getString("privateKey"), set.getBoolean("isDup"), set.getInt("trustLevel")); Logger.debug(i, "Identity found"); st.close(); return i; } st.close(); if (!create) return null; /* else we must add it, but first we need to know if it's a dup */ st = db.getConnection().prepareStatement("SELECT id FROM signatures "+ "WHERE lower(nickName) = ? LIMIT 1"); st.setString(1, nick.toLowerCase()); set = st.executeQuery(); boolean isDup = set.next(); st.close(); /* and we add */ st = db.getConnection().prepareStatement("INSERT INTO signatures "+ "(nickName, publicKey, privateKey, isDup, trustLevel) "+ "VALUES (?, ?, ?, ?, 0)"); st.setString(1, nick); st.setString(2, publicKey); st.setNull(3, Types.VARCHAR); st.setBoolean(4, isDup); st.execute(); st.close(); /* and next we find back the id */ st = db.getConnection().prepareStatement("SELECT id "+ "FROM signatures "+ "WHERE publicKey = ? LIMIT 1"); st.setString(1, publicKey); set = st.executeQuery(); set.next(); int id = set.getInt("id"); st.close(); Identity i = new Identity(db, id, nick, publicKey, null, isDup, 0); Logger.info(i, "New identity found"); Signatures.notifyPublicIdentityAdded(i); return i; } } catch(SQLException e) { Logger.error(new Identity(), "Error while getting identity (2) : "+e.toString()); e.printStackTrace(); } return null; } /** * won't create */ public static Identity getIdentity(Hsqldb db, int id) { Identity i = null; try { synchronized(db.dbLock) { PreparedStatement st; st = db.getConnection().prepareStatement("SELECT id, nickName, publicKey, "+ "privateKey, isDup, trustLevel "+ "FROM signatures "+ "WHERE id = ? LIMIT 1"); st.setInt(1, id); ResultSet set = st.executeQuery(); if (!set.next()) { st.close(); return null; } i = new Identity(db, id, set.getString("nickName"), set.getString("publicKey"), set.getString("privateKey"), set.getBoolean("isDup"), set.getInt("trustLevel")); st.close(); } } catch(SQLException e) { Logger.error(new Identity(), "Error while getting identity (1) : "+e.toString()); e.printStackTrace(); } return i; } /** * Generate a new identity * you have to insert() it after * @param db just here to fill in the class */ public static Identity generate(Hsqldb db, String nick) { Logger.info(null, "thaw.plugins.signatures.Identity : Generating new identity ..."); initFrostCrypt(); String[] keys = frostCrypt.generateKeys(); Identity identity = new Identity(db, -1, nick, keys[1], /* public */ keys[0], /* private */ false, 10); Logger.info(identity, "done"); return identity; } /** * id won't be set */ public void insert() { try { synchronized(db.dbLock) { PreparedStatement st; st = db.getConnection().prepareStatement("SELECT id FROM signatures "+ "WHERE publicKey = ? LIMIT 1"); st.setString(1, publicKey); ResultSet set = st.executeQuery(); if (set.next()) { int id = set.getInt("id"); st.close(); st = db.getConnection().prepareStatement("UPDATE signatures SET "+ "privateKey = ?, trustLevel = ? "+ "WHERE id = ?"); st.setString(1, privateKey); st.setInt(2, trustLevel); st.setInt(3, id); st.execute(); st.close(); Signatures.notifyIdentityUpdated(this); } else { st.close(); st = db.getConnection().prepareStatement("INSERT INTO signatures "+ "(nickName, publicKey, privateKey, "+ "isDup, trustLevel) "+ "VALUES (?, ?, ?, ?, ?)"); st.setString(1, nick); st.setString(2, publicKey); st.setString(3, privateKey); st.setBoolean(4, isDup); st.setInt(5, trustLevel); st.execute(); st.close(); if (privateKey == null) Signatures.notifyPublicIdentityAdded(this); else Signatures.notifyPrivateIdentityAdded(this); } } } catch(SQLException e) { Logger.error(this, "Exception while adding the identity to the bdd: "+e.toString()); e.printStackTrace(); } } public static boolean hasAtLeastATrustDefined(Hsqldb db) { try { synchronized(db.dbLock) { PreparedStatement st = db.getConnection().prepareStatement("SELECT id FROM signatures WHERE trustLevel != ? and trustLevel < ?"); st.setInt(1, 0); st.setInt(2, trustLevelInt[0]); ResultSet set = st.executeQuery(); boolean b= set.next(); st.close(); return b; } } catch(SQLException e) { Logger.error(new Identity(), "Exception while accessing the signature table : "+e.toString()); e.printStackTrace(); } return false; } public void delete() { try { synchronized(db.dbLock) { PreparedStatement st; st = db.getConnection().prepareStatement("DELETE FROM signatures "+ "WHERE id = ?"); st.setInt(1, id); st.execute(); st.close(); } } catch(SQLException e) { Logger.warning(this, "Exception while deleting the identity from the bdd: "+e.toString()); e.printStackTrace(); new thaw.gui.WarningWindow((thaw.core.MainWindow)null, I18n.getMessage("thaw.plugin.signature.delete.cant")); } } public String sign(String text) { initFrostCrypt(); return frostCrypt.detachedSign(text, privateKey); } public static String sign(String text, String privateKey) { initFrostCrypt(); return frostCrypt.detachedSign(text, privateKey); } public boolean check(String text, String sig) { try { initFrostCrypt(); return frostCrypt.detachedVerify(text, publicKey, sig); } catch(Exception e) { Logger.info(this, "Exception while checking signature: "+e.toString()); //e.printStackTrace(); return false; } } public static boolean check(String text, /* signed text */ String sig, String publicKey) /* y */ { initFrostCrypt(); return frostCrypt.detachedVerify(text, publicKey, sig); } public String toString() { String n = nick; if (n.indexOf('@') >= 0) n.replaceAll("@", "_"); return n+"@"+hash; } public static Vector getIdentities(Hsqldb db, String cond) { try { synchronized(db.dbLock) { Vector v = new Vector(); PreparedStatement st; if (cond != null) st = db.getConnection().prepareStatement("SELECT id, nickName, publicKey, "+ "privateKey, isDup, trustLevel "+ "FROM signatures "+ "WHERE "+cond + " "+ "ORDER BY LOWER(nickName)"); else st = db.getConnection().prepareStatement("SELECT id, nickName, publicKey, "+ "privateKey, isDup, trustLevel "+ "FROM signatures ORDER BY LOWER(nickName)"); ResultSet set = st.executeQuery(); while(set.next()) { v.add(new Identity(db, set.getInt("id"), set.getString("nickName"), set.getString("publicKey"), set.getString("privateKey"), set.getBoolean("isDup"), set.getInt("trustLevel"))); } st.close(); return v; } } catch(SQLException e) { Logger.error(new Identity(), "Error while getting identities (1): "+e.toString()); e.printStackTrace(); } return null; } public static Vector getYourIdentities(Hsqldb db) { return getIdentities(db, "privateKey IS NOT NULL"); } public static Vector getOtherIdentities(Hsqldb db) { return getIdentities(db, "privateKey IS NULL"); } public Element makeCDATA(Document doc, String tagName, String content) { if (content == null || tagName == null) return null; CDATASection cdata; Element current; current = doc.createElement(tagName); cdata = doc.createCDATASection(content); current.appendChild(cdata); return current; } /** * Frost format * @param file * @return */ public boolean exportIdentity(File file) { Document doc = XMLTools.createDomDocument(); Element root = doc.createElement("FrostLocalIdentities"); Element identityEl = doc.createElement("MyIdentity"); identityEl.appendChild(makeCDATA(doc, "name", toString())); identityEl.appendChild(makeCDATA(doc, "key", publicKey)); if (privateKey != null) identityEl.appendChild(makeCDATA(doc, "privKey", privateKey)); root.appendChild(identityEl); doc.appendChild(root); return XMLTools.writeXmlFile(doc, file.getPath()); } public byte[] decode(byte[] input) { initFrostCrypt(); try { return frostCrypt.decrypt(input, privateKey); } catch(Exception e) { Logger.info(this, "hm, '"+e.toString()+"' => probably not for us ("+toString()+")"); e.printStackTrace(); } return null; } public byte[] encode(byte[] input) { initFrostCrypt(); try { return frostCrypt.encrypt(input, publicKey); } catch(Exception e) { Logger.error(this, "Can't crypt message because : '"+e.toString()+"'"); e.printStackTrace(); } return null; } /** * Frost format * @param db * @param file * @return Vector<Identity> */ public static Vector importIdentity(Hsqldb db, File file) { Vector ids = new Vector(); try { Document doc = null; try { doc = XMLTools.parseXmlFile(file, false); } catch(Exception ex) { // xml format error Logger.error(ex, "Invalid Xml"); return null; } if( doc == null ) { Logger.error(null, "Error: couldn't parse XML Document - " + "File name: '" + file.getName() + "'"); return null; } Element rootEl = doc.getDocumentElement(); List l = XMLTools.getChildElementsByTagName(rootEl, "MyIdentity"); if (l == null) { Logger.error(null, "No identity to import"); return ids; } for (Iterator it = l.iterator(); it.hasNext();) { Element identityEl = (Element)it.next(); String[] split = XMLTools.getChildElementsCDATAValue(identityEl, "name").split("@"); String nick = split[0]; String publicKey = XMLTools.getChildElementsCDATAValue(identityEl, "key"); String privateKey = XMLTools.getChildElementsCDATAValue(identityEl, "privKey"); Identity identity = new Identity(db, -1, nick, publicKey, privateKey, false, 10); identity.insert(); ids.add(identity); } } catch(Exception e) { /* XMLTools throws runtime exception sometimes ... */ Logger.error(e, "Unable to parse XML message because : "+e.toString()); e.printStackTrace(); return null; } return ids; } public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof Identity)) return false; if (getId() < 0) return false; return (getId() == ((Identity)o).getId()); } }