summaryrefslogtreecommitdiff
path: root/src/org/noreply/fancydress/directory/Directory.java
blob: 01fd9dbe414ce1d3f1c1efa85484c0f316f141bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/* $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
	 * <code>m</code>.  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;
	}
}