Naming service specification, version 1.0

"wiht" <[email protected]> Fri, 08 Aug 2003 01:06:01 -0500
Newsgroups gmane.comp.security.invisiblenet.iip.devel
Message-ID <[email protected]>
The attached file contains version 1.0 of the specification for the naming service software to run over the I2P network. The idea presented in this document needs to be revised before it can be fully implemented. Nevertheless, I release this document, and hope that you can give me suggestions for improving the naming service. Please respond with your ideas either privately or to the list with your suggestions.
ns_spec.txt (text/plain, 25.5 KB)
Specification of the Naming Service for the I2P Network
Version 1.0
August 8, 2003


Contents
--------
1. Introduction
2. Data structures
3. Message structures
4. Setup of a naming server
5. How clients interact with a naming server
6. How naming servers interact with other naming servers
7. Conclusion and plans for the future

================================================================================


1. Introduction

As currently specified in the I2P Network Specification, communication between
two destinations on the network can occur only if one destination knows how to
contact the other. In other words, the first destination must know the second's
hash of the destination to communicate. This information has to be given to the
first destination before communication can occur. What if the destination is
not known, but a user of the I2P network knows that he or she wants to contact
a certain person and establish an instant messaging session?

The naming service (NS) application, running over the I2P network, solves this
problem. If user X wants to speak with user Y via Instant Messenger, user X's
IM client would look up user Y in a name server (the destination running the
naming service application), and obtain user X's contact information. If user
Y does not have IM capability, the NS would inform user X of this fact. Users
(or entities, rather) who want to simplify being contacted by others would, at
their option, register an entity name with a name server, which others could
use to look them up. Now, instead of having to type in user Y's destination
hash, user X could type in simply "user Y:im" as the destination to connect to.

The naming service, as specified below, has several capabilities. Entities
can register with it, add services they provide to their records, and modify
or delete these records. Records are signed with the entity's public key so
that only the entity establishing the record is able to modify it.

The entity establishing a record is responsible for updating the record if the
record changes. Other entities can search for whether that entity's record
exists, what services it provides, and whether it provides a particular service.


2. Data structures

The messages that clients and naming servers issue are described in sections 4
and 5 of this specification. This section specifies the formats and data types
for data structures used by the naming service software. Named constants that
appear in this section are all integers.

The two main structures the ServerInfo and EntityInfo structure, containing
records for naming servers and the entities who registered with a naming
server, respectively. The structures are as follows. The sizes of the data
members of the structures are those described in the I2P Network Specification.
All time information is specified in the UTC timezone (universal time), and
has the format "Aug 06 2003 01:23:45"--three letters for the month, two for the
day, four for the year, etc.

ServerInfo
   versionNumber - integer indicating the naming service's version
   lastUpdated - the time of last modification
   hashDest - hash of the server's destination
   pubKey - the server's public key

Service
   type - named constant (see below) indicating the type of service
   contact - hash of the destination providing this service for an entity

EntityInfo
   name - the entity's name, a string of 1-32 characters
   pubKey - public key for modifying the record
   lastUpdated - the time of last modification (set by naming service)
   numServices - number of service structures in the list of services
   service[1] - Service structure 1 (for example, for IM service)
   ...
   service[numServices] - last Service structure

The constants for the services recognized by the naming service are the
following. These are used in querying whether an entity provides a given
service. In this version, a query containing numbers other than these is
erroneous, and is discarded. In future versions, higher numbers can be used to
indicate other services over the I2P network.

1 = Instant Messaging (IM)
2 = File Transfer Protocol (FTP)
3 = Internet Relay Chat (IRC)
4 = World Wide Web (WWW)

All the incoming messages to a naming service are called requests, and all
outgoing messages are called responses. The naming service maintains a request
queue for dealing with incoming messages. In this version of the naming service
a simple first-in-first-out queue is used for messages, whatever their source.
The numer of requests handled is configurable. When the naming service starts
operating, it handles x requests at once (the number is read in from a settings
file). When requests arrive, it adds them to the queue before processing them.
The queue grows as necessary until a limit (also configurable) is reached.
After the limit on the number of requests handled is reached, if the queue is
full the server responds to new requests in one of two ways. If it does not
know about any other naming servers, it responds with a ResponseServerBusy
message. Otherwise, it respons with a ResponseUseAnotherServer message (if the
request came from a client), giving the client another server to query. The
formats for these messages are described below.

The naming service also maintains a log file. It does not log queries, but
does log when entity records are added or updated and when it lears about
other naming servers or when those servers update their information. The log
format is "[timestamp] [message]". The timestamp has the format described above
and the message can be one of the following (or can have more detail). The log
file should be named ns_log.txt.

"Entity [x] added"
"Entity [x] information updated"
"Server [x] added"
"Server [x] information updated"

The constants for the message types recognized by the naming service are the
following. The messages, described below, are divided into two categories:
client-server and server-server messages. Also, the messages are categorized as
requests to a server or responses from a server.

0 = Request
1 = Response
2 = RequestAddClient
3 = RequestAddServer
4 = RequestAddService
5 = RequestClientAdded
6 = RequestClientExists
7 = RequestClientUpdated
8 = RequestDeleteService
9 = RequestEntityInformation
10 = RequestModifyService
11 = RequestServerAdded
12 = RequestServerExists
13 = RequestServerInformation
14 = RequestServerUpdated
15 = ResponseAddApproved
16 = ResponseAddPending
17 = ResponseAddRejected
18 = ResponseChangedRecord
19 = ResponseClientDNE
20 = ResponseClientExists
21 = ResponseEntityInformation
22 = ResponseEntityList
23 = ResponseEntityService
24 = ResponseServerBusy
25 = ResponseServerDNE
26 = ResponseServerExists
27 = ResponseServerInformation
28 = ResponseUseAnotherServer


3. Message structures

All of the request and response messages derive from two base classes for
messages, Request and Response. The request messages for querying the naming
service about entities do not have to be signed by the sender, but those for
adding and mofidying records do. All of the response messages are signed by
the originating naming service with its public key. Please note that as far
as the I2P network is concerned, all of the messages below are data in I2P
message payloads.

The list of message structures and their fields follows. All of the Response*
messages derive from the Response message class, and all the Request* messages
derive from the Request message class. Thus, the fields in them are additional
to the fields in the Response and Request message classes/structures. All of
the signature fields in the structures below are signatures of the rest of
the message. For example, the ResponseServerBusy structure contains the field
reason in addition to the fields in the Response structure. They are omitted
because they are redundant.

Request - generic request message, not actually sent to servers
   msgType - type of message, an integer from the list above
   src - hash of the destination of the sender of the request
   time - timestamp of the time when the message was issued; see format above

Response
   msgType - type of message, an integer from the list above
   time - timestamp of the time when the message was issued; see format above
   signature - signature of the rest of the message

RequestAddClient
   signature - signature of the rest of the fields with entity's private key
   name - case-sensitive name of the entity to be added to naming service
   key - public key for the entity

RequestAddServer
   signature - signature of the rest of the fields with server's private key
   key - public key for the server

RequestAddService
   signature - signature of the rest of the fields with the entity's private key
   name - name of the entity for which to act
   service - Service data structure describing the service to add

RequestClientAdded
   signature - signature of the following field with naming server's private key
   info - EntityInfo structure for the entity added

RequestClientExists
   signature - signature of the server making the query, made with private key
   id - random integer identifying this request
   name - name (case-sensitive) of the entity to look up

RequestClientUpdated
   signature - signature of the following field with naming server's private key
   info - updated EntityInfo structure for an entity

RequestDeleteService
   signature - signature of the rest of the fields with the entity's private key
   name - name of the entity for which to act
   service - Service data structure to delete from the entity's record

RequestEntityInformation
   query - string specifying what information is sought; it is case-sensitive

RequestModifyService
   signature - signature of the rest of the fields with the entity's private key
   name - name of the entity for which to act
   service - Service data structure for the service to modify

RequestServerAdded
   dest - hash of the destination to send information to
   key - public key for the new server just added

RequestServerExists
   signature - signature of the server making the query, made with private key
   id - random integer identifying this request
   dest - hash of the destination for the server to look up
   key - public key of the server to look up

RequestServerInformation
   [no additional fields]

RequestServerUpdated
   signature - signature of the following field with naming server's private key
   dest - hash of the new destination for that naming server

ResponseAddApproved
   hash - hash of the destination of the entity or server to add

ResponseAddPending
   hash - hash of the destination of the entity or server to add

ResponseAddRejected
   hash - hash of the destination of the entity or server to add

ResponseChangedRecord
   hash - hash of the destination of the entity or server with changed record

ResponseClientDNE
   id - number of the request for which this is a response

ResponseClientExists
   id - number of the request for which this is a response

ResponseEntityInformation
   info - an EntityInfo data structure with the information for an entity 

ResponseEntityList
   service - type of service requested in the query
   numFound - number of entities found providing a given service; could be zero
   name[1] - name of first entity providing the service
   dest[1] - that entity's destination hash for the service
   ...
   name[numFound] - this list is only returned if 1 or more entities were found
   dest[numFound]

ResponseEntityService
   name - name of the entity for which the request was made
   service - service type in the request
   dest - hash of the destination to contact for that service

ResponseServerBusy
   reason - string explaining why server cannot fulfill a request

ResponseServerDNE
   id - number of the request for which this is a response

ResponseServerExists
   id - number of the request for which this is a response

ResponseServerInformation
   info - a ServerInfo data structure with information for the naming server

ResponseUseAnotherServer
   dest - hash of that server's destination
   key - public key for that server


4. Setup of a naming server

A naming server is the destination to which clients (and other naming servers)
connect. The naming service is the software application running on that server.
The software processes and responds to requests from clients and other servers.
Naturally, the software needs to perform setup before it can process requests.

The first part of the setup is to establish a destination and publish its lease
on the I2P network. Assuming the naming service operator also administers an
I2P router, the router needs to be set up first. Then, the naming service
software, which is really a type of I2P client, sets up a destination and
publishes a destination lease with the router. After this step, the naming
server can be connected to. It sends and receives data using the format given
in the I2P network specification.

The second part of the setup is reading in settings from a file. The file will
be named ns_prefs.txt, and will contain settings of the form "name=value", one
setting per line. Comments (beginning with "#") and blank lines will be ignored.
The naming service application will load the settings whenever it is run. This
file should be protected from viewing by the public; it should not be world
readable. A sample file appears below.

privateKey=
publicKey=
minRequests=10
maxRequests=50
logFile=filename      # Location of the file to log service's actitities to.
peer=0123456789abcdef # Sample hash of another name server's destination.
key=12983740          # Sample public key of the name server above.
peer=fedcba9876543210
key = qwepioruq

The file contains the private key and the public key values as the first two
entries. If the entries are blank, as in this sample, the public/private key
pair should be generated and written to the file. The next two settings control
the size of the request queue. The minRequests setting's value controls the
size of the queue when the server starts, and maxRequests controls the maximum
size of the queue (see the queue's description in section 2). The log file's
location on the hard drive is the next setting. The settings after that are the
list of naming servers this naming server knows about; their destinations'
hashes and public keys are added to the settings file as this naming server
learns about other naming servers on the I2P network.

After reading the settings and generating the public and private keys (if
necessary), the naming service application awaits requests to process from
clients or other naming service applications on the I2P network. Please note
that this keypair is separate from the public key of the destination on the I2P
network where the service runs. The destination can change its lease or obtain
a new one, but the service can persist and be identified by the combination of
its destination and public key.

When sending responses to requests from clients or other naming servers, the
naming service software will sign all messages. This is to prevent forging
entries from that server and to prevent man-in-the-middle attacks. When
receiving messages with signatures in them, the service will always check
signatures and discard messages whose signatures do not validate.


5. How clients interact with a naming server

Clients contact naming servers for two purposes, generally speaking. These
purposes are to add or update an entity record to the naming service or to
obtain information about another entity. But first, they may wish to obtain
a naming server's public key, to check other messages from it for validity.
This is done with a RequestServerInformation message. The response is either a
ResponseServerInformation message or a ResponseServerBusy message, in which
case the client will have to try again.

To request information about another entity, the client will send a message of
the type RequestEntityInformation. It will send a query as part of that
message consisting of two fields, separated by a colon. The first part is the
entity name to look up information for; the second is the service(s) to
search for. For example, "userx:www" is a valid query; so is "*:irc", which
asks for all entities providing the irc service; "userx:*" asks for all
information available on userx. Clients are not allowed to make the "*:*"
query, and it will be discarded.

The server's response varies, depending on the query issued to it and on the
number of queries it is processing. If its local router has detected abuse,
it will respond with a ResponseServerBusy message. Otherwise, if its queue of
requests is full, it will respond with a ResponseUseAnotherServer message,
giving the server information for another server to query. If the query is
of the type "user:*", it will return a ResponseEntityInformation message, with
all the information for that entity. If the query is of the type "userx:www",
it will return a ResponseEntityService message, containing the information for
only that entity and service. For the "*:irc" type of query, the message
returned is ResponseEntityList, with a list of destination hashes for IRC
providers. These responses can contain empty lists and empty information data
structures if nothing was found.

The other task that clients can perform is to register themselves with a naming
service, and update that information by adding or modifying their records. To
begin registration, a client will send a RequestAddClient message. This message
will contain the entity name to add, the client's public key and hash of
destination, and will be signed by the client's private key. The server will
respond with a ResponseAddPending message to the client, or ResponseServerBusy
if it is under attack. It will then synchronize with other servers, as the next
section describes, and will determine whether the entity name has already been
registered. If it has not, the client will receive a ResponseAddApproved message
from the service shortly after making the request. After that, the entity name
has been registered. The ResponseAddRejected message indicates that the entity
name already has been registered by someone else.

After registering, the client can add, delete, or mofidy services in its record
with the naming service. The messages to do so can be sent to any naming server
since servers propagate updates to the record. The messages are, respectively,
RequestAddService, RequestModifyService, and RequestDeleteService. The messages
will contain the entity name for which to perform these operations, and will
be signed by the entity's private key. The service must validate the requests,
and perform no actions if the signature does not verify using the entity's
public key.


6. How naming servers interact with other naming servers

The naming service application running on any given naming server cannot serve
the requests of all possible clients. That is why it interacts with other naming
service applications running on other naming servers. It is, in essence, a
distributed application. So naming servers need to interact with each other,
and they do so by sending messages to each other. The cases in which naming
servers need to send messages are when a naming server joins the network of
naming servers, when its information (destination and in later versions possibly
the public key) changes, when an entity is registered with a naming server, and
when an entity's information is updated. In the network of naming servers, each
naming server knows about all the other naming servers.

When a naming server wants to join the network, it will need to know the hash
of the destination for one of the servers already in the network. It first
needs to obtain the server's public key, by sending a RequestServerInformation
message. The response is either a ResponseServerInformation message or a
ResponseServerBusy message, in which case the server will have to try again at
another time. After obtaining the public key of the server to which it will
make the request to join the network, the new server sends a RequestAddServer
message, with its hash of the destination and public key, signed by its private
key. The server then broadcasts a RequestServerExists query to every other
server on the list. It waits for a reasonable amount of time, and if some of
the servers do not respond, it assumes the entry does not exist on their lists.
The other servers respond with ResponseServerExists if the server is in their
lists, and ResponseServerDNE if not. If any of the other servers responds with
a ResponseServerExists message, the new server is sent a ResponseAddRejected
message indicating it will not be added. Otherwise, after receiving the other
servers' responses, it responds to the new server with a ResponseAddApproved
message. Each RequestServerExists message has a unique ID number, so that the
server can discard response messages with that ID number if it rejects the
addition of the new server.
 
After the new server is added, it needs to be integrated into the network of
naming servers. The server that approved the addition then broadcasts to every
server in the network a RequestServerAdded message, indicating the server whose
list of servers needs to be populated. Every server then sends its own
information in a ResponseServerInformation message to the new server, which
builds a list of servers from these messages. The new server now needs to obtain
the data about entities that it will send to clients. It picks a random server
and sends it a RequestEntityInformation with a query of "*:*". That server then
sends it a number of ResponseEntityInformation messages for every registered
entity it knows about. The new server receives this information, and after it
receives all these messages it knows about every entity. It can now serve
client requests.

The new server in the network can give its information to entities to use it
as their naming server. It can also rely on eventually being contacted. When one
of the other naming servers sends a ResponseUseAnotherServer message, it now
has a chance of being the server clients are told to contact, and it will
process the clients' requests.

When an entity attempts to register itself with the network of naming servers,
the process is similar to that of a server joining the network. The entity
sends a RequestAddClient message, and the naming server responds with a
ResponseAddPending message. It then broadcasts a RequestClientExists query to
the other naming servers. Similarly to the description above, it will respond
to the entity with a ResponseAddRejected (if any other naming server responds
with a ResponseClientExists message) or ResponseAddApproved (if all the other
servers respond with a ResponseClientDNE message). After the addition of the
entity is approved, the server that approved the addition broadcasts a
RequestClientAdded message with the entity's information. The other servers do
not respond to this message, but add the entity to their lists of entities.

When an entity wishes to add, modify, or delete a service in the list of
services it provides, it sends an appropriate message to the naming service. If
the naming service responds with a ResponseUseAnotherServer message or with a
ResponseServerBusy message, the client should try again. If it receives no
response, then its signature was wrong, and the message was discarded. When a
change in the record is successful, the naming service does two things. First,
it replies to the entity/client with a ResponseChangedRecord message. Second,
it broadcasts a RequestClientUpdated message to the other servers with the
changed record. They do not respond, but change their records for that entity.

Finally, if a naming service has had its naming server under attack for some
time, it may wish to change its destination to avoid future attacks. In this
case, it will need to broadcast a RequestServerUpdated message to the other
servers, signed with its private key. The servers should each respond with a
ResponseChangedRecord message after processing the update.


7. Conclusion and plans for the future

This version omits certain suggestions that have been made on this mailing list
and in IIP discussions. These ideas will likely be incorporated in future
versions of the naming service. One idea is using a Resource Description
Framework (see http://www.w3.org/RDF) together with the naming service. As I
am not familiar with RDF at this time, I have not included it in this
specification. Another idea is having Certificate Authorities (CAs) that sign
entity entries when entities are registered with naming servers. A possible
implementation of this idea is to have trusted naming servers that other
naming servers connect to; these servers handle entity additions and sign
entity entries. This idea is omitted for now, so as not to complicate the
design at this stage. Yet another idea I have been given is to have clients
pay with HashCash for using the naming servers. It may make sense to add this
capability at a later stage, though I think that free naming servers will make
users of the I2P network not want to use the ones where payment is required.
Finally (although this is my own suggestion), perhaps name servers should
periodically check that other servers are still operating (ping them somehow)
to detect a name server that is no longer in service.

If this document contains any errors, unclear wording, or omissions, please
let me know. I also welcome your suggestion for improving the naming service
described in this specification. I will correct the errors in version 1.1 of
this specification. My plan is to make the corrections, publish version 1.1 of
the naming service specification, then implement the naming service software.
If this specification needs to be revised again (with version 1.2) before it
can be implemented, then I will do so. The naming service capabilities described
in the sections of this document above will be coded first, then the
capabilities in the paragraph above will be gradually added.