/* $Id$ */ package org.noreply.fancydress.directory; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.crypto.InvalidCipherTextException; import org.noreply.fancydress.directory.parser.*; import org.noreply.fancydress.misc.Util; import org.noreply.fancydress.crypto.*; import org.noreply.fancydress.status.*; import java.util.*; /** * This class represents a type III Directory. * * A directory holds information about the state of a type III network, like * recommended servers, which servers exist, their keys, etc. * * FIXME: * This whole flex and cup thing was just a nice way to try these tools. * Eventually this should be rewritten to a simple parser in Java itself. * Writing it should be pretty straight forward and cut down the * dependencies. * * @see Server */ public class Directory { /** * Hash holding all Servers. * * This hash holds all Servers. Since nickname are to be treaded case * insensitive the hash's keys are the lowercased nicknames of the Servers. * * All values in this hash are instances of Server. * * @see Server */ private Hashtable byName; /** * Possibly add a new server descriptor to the directory. * * This method adds a server desccriptor to the directory if it * - conforms to the syntax specified. * - the server is not yet known or it's identity key matches the key of the already known ServerDescriptor. * * @param server a DirectoryMessage consisting of one of more sections. At 'Server' section needs to be present. * @throws Mix3BadServerFormatException if a section is syntactially invalid and the error cannot be ignored. * @throws Mix3BadServerSignatureException if the server descriptor's signature is invalid. */ private void addServerDescriptor(DirectoryMessage server) throws Mix3BadServerFormatException, Mix3BadServerSignatureException { try { ServerDescriptor sd = new ServerDescriptor(server); String key = sd.getNickname().toLowerCase(); if (byName.containsKey(key)) { Server s = (Server) byName.get(key); try { s.addDescriptor(sd); } catch (Mix3BadServerFormatException e) { System.err.println("Ignoring Descriptor with different identity for "+key); } } else { Server s = new Server(sd); byName.put(key, s); } } catch (Mix3BadServerUnrecognizedVersionException e) { System.err.println("Ignoring unregonized version"); } } /** * Create a directory from an entire DirectoryMessage. * * This constructor builds up the directory from the message * m. It optionally checks the directory's signature and * splits the message into single server descriptors to load them. * * @param m the directory message * @param checkDirectorySignature whether or not to check the directory's signature * @throws Mix3BadDirectorySignatureException if the directory's signature is invalid. * @throws Mix3BadServerSignatureException if a server descriptor's signature is invalid. * @throws Mix3BadServerFormatException if a section is syntactially invalid and the error cannot be ignored. */ public Directory(DirectoryMessage m, boolean checkDirectorySignature) throws Mix3BadDirectorySignatureException, Mix3BadServerFormatException, Mix3BadServerSignatureException { byName = new Hashtable(); if (checkDirectorySignature) checkSignature(m); DirectoryMessage server = null; for (Iterator i = m.getSectionsIterator(); i.hasNext(); ) { DirectorySection section = (DirectorySection) i.next(); if (section.getName().equals("Server")) { if (server != null) addServerDescriptor(server); server = new DirectoryMessage(); } if (server != null) server.addSection(section); } if (server != null) addServerDescriptor(server); System.out.println(byName); } /* FIXME: known directory servers should be passed as arguments */ /** * Check the signature of a type III directory. */ private void checkSignature(DirectoryMessage m) throws Mix3BadDirectorySignatureException { /* FIXME: handle more than one signature block, */ DirectorySection signatureSection = m.getSection("Signature"); if (signatureSection == null) throw new Mix3BadDirectorySignatureException("No Signature section found."); DirectoryEntry signatureEntry = signatureSection.getEntry("DirectorySignature"); if (signatureEntry == null) throw new Mix3BadDirectorySignatureException("No DirectorySignature in Signature section found."); DirectoryEntry identityEntry = signatureSection.getEntry("DirectoryIdentity"); if (identityEntry == null) throw new Mix3BadDirectorySignatureException("No DirectoryIdentity in Signature section found."); byte[] signature = Base64.decode(signatureEntry.getValue()); byte[] identity = Base64.decode(identityEntry.getValue()); DirectoryMessage cleanedDirectory = new DirectoryMessage(m); cleanedDirectory.blindValue("Signature","DirectoryDigest"); cleanedDirectory.blindValue("Signature","DirectorySignature"); /* FIXME: what if identity is no valid key? */ RSAPublicKey identityKey = new RSAPublicKey(identity); boolean verifies; try { verifies = identityKey.verify(signature, Util.toOctets(cleanedDirectory.toString())); } catch (InvalidCipherTextException e) { throw new Mix3BadDirectorySignatureException("Directory signature has invalid format.", e); }; if (!verifies) throw new Mix3BadDirectorySignatureException("Directory signature does not verify."); } /** * Get a server by nickname. */ public Server getServer(String name) { String key = name.toLowerCase(); if (byName.containsKey(key)) return (Server) byName.get(key); return null; } }