Security in TomP2P

Security features are typically needed in uncontrolled environments, e.g., the Internet. The built-in security features of TomP2P (>= 3.1.10) are signature-based. Encryption-based security is not built-in, but a user can encrypt its data if necessary. Thus, the following sections explain how to use the signature-based security features. For more information about signing and encryption in P2P, please see Secure routing for structured peer-to-peer overlay networks.

Signatures

Two types of signatures exist in TomP2P: message signatures and data signatures. While the message signature signs the complete message including header (IP and peerID), the data signature only signs the data object.

Data Signatures

Any data objects can be signed. For example the following object Data data=new Data("content"); is signed with data.signAndSetPublicKey(keyPair);. Thus, the data can be signed with a different keys as the message signature. As the name signAndSetPublicKey suggests, the data object is signed and the public key is attached (self-signature). A recipient of this data object can verify if the data object is signed with public key provided and compare the received public key with already stored public keys. A public key is stored on first contact and any subsequent storage, removal, etc. can be verified if its the same peer.

Data signatures are typically used in the Internet with active replication, where peers move around data objects. A message signature would not work in such a situation, since a message signature always includes the IP and the peerID of the sender, and this can change with active replication.

Message Signatures

Message signatures sign the complete message. Messages that can be signed are remove, add, store, move, get, and copy messages. These signatures are used to protect entries, excpet the get message. An example scenario, where such a protection is necessary, is in a tracker, where peers announces its availability of a file. For non-protected entries, anyone could overwrite those entries making an attack easy. Thus, the protection works that the entry can only be modified by the original author. Data and message signatures have been separated since signed data objects that should replace signed messages would require additional mechanisms such as message type indication, or peerID identification, etc.

Domain and Entry Protection Mechanisms

With signed messages, domains and entries can be protected. Protected means, that nobody else can overwrite it.

The following modes and methods exist for domains and entries:

The following modes and methods exist for entries in unprotected domains:

The following modes exist for entries in a protected domain:

If a protected domain has no entries, i.e., they all expired, the domain gets unprotected. The following table describes those modes and methods.

NO_*_MASTER or MASTER_*_PUBLIC_KEYThis mode, in both domain and entry specifies, if a hash of the public key of a signed message can overwrite a currently preset value. NO_MASTER cannot overwrite, while MASTER_PUBLIC_KEY can, even if the domain is protected.
DOMAIN_PROTECTION_ALL or DOMAIN_PROTECTION_NONEThis modes either sets all domains, which can be protected by any peer, or none.
removeDomainProtection(x)This methods removes domains, which can be protected. The opposite addDomainProtection does not make much sense here, because every peer has to specify it and is publicly known, which makes it a perfect target for malicious peers.
ENTRY_PROTECTION_ALL or ENTRY_PROTECTION_NONEThis mode turns of or on protection for entries.

These combinations are possible:

Examples

These modes are set by default, which should be fine for most applications.

If other values should be set, e.g., disable domain protection, use setProtection(protectionDomainEnable, protectionDomainMode, protectionEntryEnable, protectionEntryMode, protectionEntryInDomain).

A peer can protect a domain as follows:

FutureDHT futureDHT = peer1.put(Number160.ONE).setData(new Data("test"))
  .setProtectDomain().setDomainKey(Number160.ZERO).start();

The domain with the number peer2Owner is now protected and only peer1 can use this domain for data storage for any content key if the master public key mode is not set. If it is set, the peer with the hash of the public key equals 0, can overtake this domain as shown in the next example:

KeyPairGenerator gen = KeyPairGenerator.getInstance( "DSA" );
KeyPair pair2 = gen.generateKeyPair();
final Number160 peer2Owner = Utils.makeSHAHash( pair2.getPublic().getEncoded()); // results in 0
FutureDHT futureDHT = peer1.put(Number160.ONE).setData(new Data("test"))
  .setProtectDomain().setDomainKey(peer2Owner).start();

Domains can also be set to never be protected with storage.removeDomainProtection(new Number160(11));. Also, a public key with this hash cannot protect this domain if the domain is set to unprotectable. The entry protection works similar:

Data data=new Data("test1");
data.setProtectedEntry(true);
FutureDHT futureDHT = peer1.put(Number160.ONE).setData(data)
  .setProtectDomain().setDomainKey(peer2Owner).start();

The entry with the key 12 can only be changed by the peer_xy if the master content key mode is not set.

Tracker Security

A tracker has the built in modes: NO_DOMAIN_MASTER, DOMAIN_PROTECTION_NONE, ENTRY_PROTECTION_NONE, MASTER_ENTRY_PUBLIC_KEY, which means that if the peer decides to protect its entry on the tracker, the key has to be the hash of its public key and the add message has to be signed. This can be done by calling TrackerBuilder.setSign(true);.