Inventory items are now saved only if processing didn't fail. Receiving messages works, but there seems to be a problem with the POW check in some circumstances.

This commit is contained in:
2015-05-23 10:27:05 +02:00
parent 6b3b361aa3
commit b793526f2f
22 changed files with 82 additions and 73 deletions

View File

@ -142,11 +142,7 @@ public class BitmessageContext {
}
public void startup(Listener listener) {
MessageListener messageListener = new DefaultMessageListener(ctx, listener);
for (ObjectMessage object : ctx.getInventory().getObjects(0, 0, PUBKEY, MSG)) {
messageListener.receive(object);
}
ctx.getNetworkHandler().start(messageListener);
ctx.getNetworkHandler().start(new DefaultMessageListener(ctx, listener));
}
public void shutdown() {

View File

@ -20,6 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.ports.NetworkHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -28,6 +29,7 @@ import java.io.IOException;
import java.util.List;
import static ch.dissem.bitmessage.entity.Plaintext.Status.DOING_PROOF_OF_WORK;
import static ch.dissem.bitmessage.entity.Plaintext.Status.NEW;
import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
@ -42,7 +44,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
}
@Override
public void receive(ObjectMessage object) {
public void receive(ObjectMessage object) throws IOException {
ObjectPayload payload = object.getPayload();
if (payload.getType() == null) return;
@ -74,7 +76,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
}
}
protected void receive(ObjectMessage object, Pubkey pubkey) {
protected void receive(ObjectMessage object, Pubkey pubkey) throws IOException {
BitmessageAddress address;
try {
if (pubkey instanceof V4Pubkey) {
@ -106,26 +108,28 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
ctx.getMessageRepository().save(msg);
}
}
} catch (IllegalArgumentException | IOException e) {
LOG.debug(e.getMessage(), e);
} catch (DecryptionFailedException ignore) {
LOG.debug(ignore.getMessage(), ignore);
}
}
protected void receive(ObjectMessage object, Msg msg) {
protected void receive(ObjectMessage object, Msg msg) throws IOException {
for (BitmessageAddress identity : ctx.getAddressRepo().getIdentities()) {
try {
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
msg.getPlaintext().setTo(identity);
object.isSignatureValid(msg.getPlaintext().getFrom().getPubkey());
msg.getPlaintext().setStatus(NEW);
ctx.getMessageRepository().save(msg.getPlaintext());
listener.receive(msg.getPlaintext());
break;
} catch (IOException | RuntimeException ignore) {
} catch (DecryptionFailedException ignore) {
LOG.debug(ignore.getMessage(), ignore);
}
}
}
protected void receive(ObjectMessage object, Broadcast broadcast) {
protected void receive(ObjectMessage object, Broadcast broadcast) throws IOException {
// TODO this should work fine as-is, but checking the tag might be more efficient
// V5Broadcast v5 = broadcast instanceof V5Broadcast ? (V5Broadcast) broadcast : null;
for (BitmessageAddress subscription : ctx.getAddressRepo().getSubscriptions()) {
@ -133,7 +137,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
broadcast.decrypt(subscription.getPubkeyDecryptionKey());
object.isSignatureValid(broadcast.getPlaintext().getFrom().getPubkey());
listener.receive(broadcast.getPlaintext());
} catch (IOException ignore) {
} catch (DecryptionFailedException ignore) {
}
}
}

View File

@ -16,6 +16,8 @@
package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import java.io.IOException;
/**
@ -24,7 +26,7 @@ import java.io.IOException;
public interface Encrypted {
void encrypt(byte[] publicKey) throws IOException;
void decrypt(byte[] privateKey) throws IOException;
void decrypt(byte[] privateKey) throws IOException, DecryptionFailedException;
boolean isDecrypted();
}

View File

@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.payload.ObjectType;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.utils.Bytes;
import ch.dissem.bitmessage.utils.Encode;
import ch.dissem.bitmessage.utils.Security;
@ -116,13 +117,13 @@ public class ObjectMessage implements MessagePayload {
}
}
public void decrypt(PrivateKey key) throws IOException {
public void decrypt(PrivateKey key) throws IOException, DecryptionFailedException {
if (payload instanceof Encrypted) {
((Encrypted) payload).decrypt(key.getPrivateEncryptionKey());
}
}
public void decrypt(byte[] privateEncryptionKey) throws IOException {
public void decrypt(byte[] privateEncryptionKey) throws IOException, DecryptionFailedException {
if (payload instanceof Encrypted) {
((Encrypted) payload).decrypt(privateEncryptionKey);
}

View File

@ -92,7 +92,7 @@ public class Plaintext implements Streamable {
public void setTo(BitmessageAddress to) {
if (this.to.getVersion() != 0)
throw new RuntimeException("Correct address already set");
if (Arrays.equals(this.to.getRipe(), to.getRipe())) {
if (!Arrays.equals(this.to.getRipe(), to.getRipe())) {
throw new RuntimeException("RIPEs don't match");
}
this.to = to;
@ -210,7 +210,7 @@ public class Plaintext implements Streamable {
SENT_ACKNOWLEDGED,
// For received messages
RECEIVED,
NEW,
READ
}
@ -348,9 +348,6 @@ public class Plaintext implements Streamable {
}
public Plaintext build() {
if (id == null) {
id = UUID.randomUUID();
}
if (from == null) {
from = new BitmessageAddress(Factory.createPubkey(
addressVersion,

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload;
import ch.dissem.bitmessage.entity.Encrypted;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import java.io.IOException;
@ -51,7 +52,7 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted {
}
@Override
public void decrypt(byte[] privateKey) throws IOException {
public void decrypt(byte[] privateKey) throws IOException, DecryptionFailedException {
plaintext = Plaintext.read(encrypted.decrypt(privateKey));
}

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.entity.payload;
import ch.dissem.bitmessage.entity.Streamable;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.utils.*;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
@ -99,7 +100,7 @@ public class CryptoBox implements Streamable {
/**
* @see <a href='https://bitmessage.org/wiki/Encryption#Decryption'>https://bitmessage.org/wiki/Encryption#Decryption</a>
*/
public InputStream decrypt(byte[] privateKey) {
public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException {
// 1. The private key used to decrypt is called k.
BigInteger k = Security.keyToBigInt(privateKey);
// 2. Do an EC point multiply with private key k and public key R. This gives you public key P.
@ -113,7 +114,7 @@ public class CryptoBox implements Streamable {
// 5. Calculate MAC' with HMACSHA256, using key_m as salt and IV + R + cipher text as data.
// 6. Compare MAC with MAC'. If not equal, decryption will fail.
if (!Arrays.equals(mac, calculateMac(key_m))) {
throw new RuntimeException("Invalid MAC while decrypting");
throw new DecryptionFailedException();
}
// 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload;
import ch.dissem.bitmessage.entity.Encrypted;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import java.io.IOException;
import java.io.InputStream;
@ -85,7 +86,7 @@ public class Msg extends ObjectPayload implements Encrypted {
}
@Override
public void decrypt(byte[] privateKey) throws IOException {
public void decrypt(byte[] privateKey) throws IOException, DecryptionFailedException {
plaintext = Plaintext.read(encrypted.decrypt(privateKey));
}

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Encrypted;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.utils.Decode;
import java.io.IOException;
@ -65,7 +66,7 @@ public class V4Pubkey extends Pubkey implements Encrypted {
}
@Override
public void decrypt(byte[] privateKey) throws IOException {
public void decrypt(byte[] privateKey) throws IOException, DecryptionFailedException {
decrypted = V3Pubkey.read(encrypted.decrypt(privateKey), stream);
}

View File

@ -23,9 +23,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
/**
* Created by chris on 13.03.15.
*/
public class InventoryVector implements Streamable {
/**
* Hash of the object

View File

@ -0,0 +1,4 @@
package ch.dissem.bitmessage.exception;
public class DecryptionFailedException extends Exception {
}

View File

@ -21,6 +21,8 @@ import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import java.io.IOException;
/**
* Handles incoming messages
*/
@ -32,6 +34,6 @@ public interface NetworkHandler {
void offer(InventoryVector iv);
interface MessageListener {
void receive(ObjectMessage object);
void receive(ObjectMessage object) throws IOException;
}
}

View File

@ -17,7 +17,7 @@
package ch.dissem.bitmessage.utils;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
import org.bouncycastle.asn1.x9.X9ECParameters;
@ -35,7 +35,6 @@ import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.PrivateKey;
import java.security.spec.KeySpec;
import java.util.Arrays;
@ -110,11 +109,10 @@ public class Security {
* @throws IOException if proof of work doesn't check out
*/
public static void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException {
if (Bytes.lt(
getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes),
Security.doubleSha512(object.getNonce(), getInitialHash(object)),
8)) {
throw new IOException("Insufficient proof of work");
byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes);
byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object));
if (Bytes.lt(target, value, 8)) {
throw new IOException("Insufficient proof of work: " + Strings.hex(target) + " required, " + Strings.hex(Arrays.copyOfRange(value, 0, 8)) + " achieved.");
}
}

View File

@ -23,6 +23,7 @@ import ch.dissem.bitmessage.entity.payload.CryptoBox;
import ch.dissem.bitmessage.entity.payload.GenericPayload;
import ch.dissem.bitmessage.entity.payload.Msg;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.utils.Security;
import ch.dissem.bitmessage.utils.TestUtils;
import org.junit.Test;
@ -35,7 +36,7 @@ import static org.junit.Assert.assertTrue;
public class EncryptionTest {
@Test
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException {
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException {
GenericPayload before = new GenericPayload(1, Security.randomBytes(100));
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
@ -47,7 +48,7 @@ public class EncryptionTest {
}
@Test
public void ensureMessageCanBeDecrypted() throws IOException {
public void ensureMessageCanBeDecrypted() throws IOException, DecryptionFailedException {
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey"));
BitmessageAddress identity = new BitmessageAddress(privateKey);
assertEquals("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8", identity.getAddress());

View File

@ -24,6 +24,7 @@ import ch.dissem.bitmessage.entity.payload.ObjectType;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.utils.TestUtils;
import org.junit.Test;
@ -56,23 +57,15 @@ public class SignatureTest {
}
@Test
public void ensureMessageIsProperlySigned() throws IOException {
public void ensureMessageIsProperlySigned() throws IOException, DecryptionFailedException {
BitmessageAddress identity = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8");
ObjectMessage object = TestUtils.loadObjectMessage(3, "V1Msg.payload");
Msg msg = (Msg) object.getPayload();
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
Plaintext plaintext = msg.getPlaintext();
assertEquals(0, object.getExpiresTime());
assertEquals(loadPubkey(), plaintext.getFrom().getPubkey());
assertEquals(TestUtils.loadContact().getPubkey(), plaintext.getFrom().getPubkey());
assertNotNull(plaintext);
assertTrue(object.isSignatureValid(plaintext.getFrom().getPubkey()));
}
private V4Pubkey loadPubkey() throws IOException {
BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload");
object.decrypt(address.getPubkeyDecryptionKey());
return (V4Pubkey) object.getPayload();
}
}

View File

@ -19,6 +19,7 @@ package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.utils.*;
import org.junit.Test;
@ -80,7 +81,7 @@ public class BitmessageAddressTest {
}
@Test
public void testV4PubkeyImport() throws IOException {
public void testV4PubkeyImport() throws IOException, DecryptionFailedException {
BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload");
object.decrypt(address.getPubkeyDecryptionKey());

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.utils.TestUtils;
import org.junit.Test;
@ -76,7 +77,7 @@ public class SerializationTest {
}
@Test
public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws IOException {
public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws IOException, DecryptionFailedException {
Plaintext p1 = new Plaintext.Builder()
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
.to(TestUtils.loadContact())

View File

@ -20,6 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.factory.Factory;
import java.io.ByteArrayInputStream;
@ -68,7 +69,7 @@ public class TestUtils {
return identity;
}
public static BitmessageAddress loadContact() throws IOException {
public static BitmessageAddress loadContact() throws IOException, DecryptionFailedException {
BitmessageAddress address = new BitmessageAddress("BM-2cXxfcSetKnbHJX2Y85rSkaVpsdNUZ5q9h");
ObjectMessage object = TestUtils.loadObjectMessage(4, "V4Pubkey.payload");
object.decrypt(address.getPubkeyDecryptionKey());