/* $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 org.noreply.fancydress.type3.*; import java.util.*; import java.text.ParseException; /** * A single Server Descriptor of a type III node. * * This class holds all information of a single server descriptor. It may also * hold subsections like Incoming/MMTP or Delivery/MBOX if such sections exist * in this server descriptor. * * An instance of Server pools ServerDescriptors of a single node. * * @see Server */ public class ServerDescriptor { /* ปญญญญญญญ * descriptor version we understand. ปญญญญญญญ */ private static final String DESCRIPTOR_VERSION = "0.2"; /* Required */ private String descriptorVersion; private String nickname; private byte[] identity; private RSAPublicKey identityKey; private byte[] identityDigest; private byte[] digest; private byte[] signature; private Date published; private Date validAfter; private Date validUntil; private RSAPublicKey packetKey; private String[] packetVersions; /* Optional */ private String contact; private String contactFingerprint; private String comments; private String software; private Boolean secureConfiguration; private String whyInsecure; private IncomingMMTPSection incomingMMTPSection; private OutgoingMMTPSection outgoingMMTPSection; private DeliveryMBOXSection deliveryMBOXSection; private DeliverySMTPSection deliverySMTPSection; private Object deliveryFragmentedSection; /** * Construct a ServerDescriptor instance from the message in m. * * This constructor builds up a ServerDescriptor from the message * m which contains a single server descriptor. * * The DirectoryMessage needs to have a 'Server' section. * * @param server a DirectoryMessage consisting of one of more sections. * @throws Mix3BadServerFormatException if a section is syntactially invalid and the error cannot be ignored. * @throws Mix3BadServerSignatureException if the server descriptor's signature is invalid. */ public ServerDescriptor(DirectoryMessage m) throws Mix3BadServerFormatException, Mix3BadServerSignatureException { DirectorySection serverSection = m.getSection("Server"); if (serverSection == null) throw new Mix3BadServerFormatException("No Server section found."); parseServerSection(serverSection); identityKey = new RSAPublicKey(identity); identityDigest = CryptoPrimitives.hash(identity); checkSignature(m); DirectorySection section; section = m.getSection("Incoming/MMTP"); try { incomingMMTPSection = section == null ? null : new IncomingMMTPSection(section); } catch (Mix3BadServerFormatException e) { System.err.println(e); }; section = m.getSection("Outgoing/MMTP"); try { outgoingMMTPSection = section == null ? null : new OutgoingMMTPSection(section); } catch (Mix3BadServerFormatException e) { System.err.println(e); }; section = m.getSection("Delivery/MBOX"); try { deliveryMBOXSection = section == null ? null : new DeliveryMBOXSection(section); } catch (Mix3BadServerFormatException e) { System.err.println(e); }; section = m.getSection("Delivery/SMTP"); try { deliverySMTPSection = section == null ? null : new DeliverySMTPSection(section); } catch (Mix3BadServerFormatException e) { System.err.println(e); }; } /** * Check if s is a valid nickname. * * @throws Mix3BadServerFormatException if s is not a valid nickame */ private static void checkSyntaxNickname(String s) throws Mix3BadServerFormatException { for (int i=0; i= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '@' || c == '-')) throw new Mix3BadServerFormatException("Illegal characters in nickname: "+c); } } /** * Tokenize a Packet-Versions string into single Packet-Versions. * * The Packet-Versions is a comma separated list of Packet-Versions. * This method parses a Packet-Versions value and returns an array of * Strings, holding one Packet-Version each. * * Whitespace is trimmed off each Packet-Version. * * @param s the comma separated list of Packet-Versions * @return array of Packet-Versions. */ public static String[] parsePacketVersions(String s) { ArrayList versions = new ArrayList(); int indexFrom = 0; int indexOf; String[] tokens = Util.tokenize(s, ','); for (int i=0; i 128) throw new Mix3BadServerFormatException("Contact is too long for "+nickname); contactFingerprint = entryContactFingerprint == null ? null : entryContactFingerprint.getValue(); if (contactFingerprint != null && contactFingerprint.length() > 128) throw new Mix3BadServerFormatException("Contact-Fingerprint is too long for "+nickname); comments = entryComments == null ? null : entryComments.getValue(); if (comments != null && comments.length() >= 1024) throw new Mix3BadServerFormatException("Comments is too long for "+nickname); software = entrySoftware == null ? null : entrySoftware.getValue(); try { secureConfiguration = entrySecureConfiguration == null ? null : new Boolean(Util.parseBoolean(entrySecureConfiguration.getValue())); } catch (ParseException e) { throw new Mix3BadServerFormatException("Cannot parse SecureConfiguration for "+nickname, e); }; whyInsecure = entryWhyInsecure == null ? null : entryWhyInsecure.getValue(); } /** * Check the signature of the server descriptor in m. * * @param m the ServerDescriptor to check * @throws Mix3BadServerSignatureException if the signature is invalid. */ private void checkSignature(DirectoryMessage m) throws Mix3BadServerSignatureException { DirectoryMessage cleanedDescriptor = new DirectoryMessage(m); cleanedDescriptor.blindValue("Server","Digest"); cleanedDescriptor.blindValue("Server","Signature"); boolean verifies; try { verifies = identityKey.verify(signature, Util.toOctets(cleanedDescriptor.toString())); } catch (InvalidCipherTextException e) { throw new Mix3BadServerSignatureException("Server signature has invalid format.", e); }; if (!verifies) throw new Mix3BadServerSignatureException("Server signature does not verify."); } /** * Get this server descriptor's nickname. * * @return nickname of the server */ public String getNickname() { return nickname; } /** * Get this server descriptor's identity key. * * @return asn1 encoded identity key of the server */ public byte[] getIdentity() { return identity; } /** * Get this server descriptor's identity key. * * @return identity key of the server */ public RSAPublicKey getIdentityKey() { return identityKey; } /** * Get this server descriptor's packet key. * * @return packet key of this server descriptor */ public RSAPublicKey getPacketKey() { return packetKey; } /** * Get this server descriptor's supported packet versions. * * @return supported packet versions */ public String[] getPacketVersions() { return packetVersions; } /** * Get the date after which this server descriptor is valid. * * @return date after which this server descriptor is valid */ public Date getValidAfter() { return validAfter; } /** * Get the date until which this server descriptor is valid. * * @return date until which this server descriptor is valid */ public Date getValidUntil() { return validUntil; } /** * Get the date when this server descriptor was published. * * @return date when this server descriptor was published */ public Date getPublished() { return published; } /** * Get the Incoming/MMTP section. * * @return this server descriptor's Incoming/MMTP section */ public IncomingMMTPSection getIncomingMMTPSection() { return incomingMMTPSection; } /** * Get the Outgoing/MMTP section. * * @return this server descriptor's Outgoing/MMTP section */ public OutgoingMMTPSection getOutgoingMMTPSection() { return outgoingMMTPSection; } }