diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java index 0e03880..416e7b7 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Application.java @@ -20,7 +20,7 @@ import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.Pubkey; -import ch.dissem.bitmessage.networking.NetworkNode; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.repository.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +45,7 @@ public class Application { .inventory(new JdbcInventory(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) - .networkHandler(new NetworkNode()) + .networkHandler(new DefaultNetworkHandler()) .port(48444) .build(); diff --git a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java index 4ba8fa7..7f934a3 100644 --- a/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java +++ b/demo/src/main/java/ch/dissem/bitmessage/demo/Main.java @@ -17,7 +17,7 @@ package ch.dissem.bitmessage.demo; import ch.dissem.bitmessage.BitmessageContext; -import ch.dissem.bitmessage.networking.NetworkNode; +import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.repository.*; import ch.dissem.bitmessage.wif.WifExporter; import ch.dissem.bitmessage.wif.WifImporter; @@ -49,7 +49,7 @@ public class Main { .inventory(new JdbcInventory(jdbcConfig)) .nodeRegistry(new MemoryNodeRegistry()) .messageRepo(new JdbcMessageRepository(jdbcConfig)) - .networkHandler(new NetworkNode()) + .networkHandler(new DefaultNetworkHandler()) .port(48444) .build(); diff --git a/domain/build.gradle b/domain/build.gradle index eb83499..6bcbe6d 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -12,6 +12,7 @@ uploadArchives { dependencies { compile 'org.slf4j:slf4j-api:1.7.12' - compile 'org.bouncycastle:bcprov-jdk15on:1.52' - testCompile group: 'junit', name: 'junit', version: '4.11' -} \ No newline at end of file + testCompile 'junit:junit:4.11' + testCompile 'org.mockito:mockito-core:1.10.19' + testCompile project(':security-bc') +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java index 7878ada..d7c04b5 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/BitmessageContext.java @@ -27,12 +27,13 @@ import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.*; import ch.dissem.bitmessage.utils.Property; -import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import static ch.dissem.bitmessage.entity.Plaintext.Status.*; import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; @@ -57,12 +58,17 @@ public class BitmessageContext { public static final int CURRENT_VERSION = 3; private final static Logger LOG = LoggerFactory.getLogger(BitmessageContext.class); + private final ExecutorService pool; + private final InternalContext ctx; private Listener listener; private BitmessageContext(Builder builder) { ctx = new InternalContext(builder); + // As this thread is used for parts that do POW, which itself uses parallel threads, only + // one should be executed at any time. + pool = Executors.newFixedThreadPool(1); } public AddressRepository addresses() { @@ -74,7 +80,7 @@ public class BitmessageContext { } public BitmessageAddress createIdentity(boolean shorter, Feature... features) { - BitmessageAddress identity = new BitmessageAddress(new PrivateKey( + final BitmessageAddress identity = new BitmessageAddress(new PrivateKey( shorter, ctx.getStreams()[0], ctx.getNetworkNonceTrialsPerByte(), @@ -82,8 +88,12 @@ public class BitmessageContext { features )); ctx.getAddressRepo().save(identity); - // TODO: this should happen in a separate thread - ctx.sendPubkey(identity, identity.getStream()); + pool.submit(new Runnable() { + @Override + public void run() { + ctx.sendPubkey(identity, identity.getStream()); + } + }); return identity; } @@ -91,63 +101,71 @@ public class BitmessageContext { // TODO } - public void broadcast(BitmessageAddress from, String subject, String message) { - // TODO: all this should happen in a separate thread - Plaintext msg = new Plaintext.Builder(BROADCAST) - .from(from) - .message(subject, message) - .build(); + public void broadcast(final BitmessageAddress from, final String subject, final String message) { + pool.submit(new Runnable() { + @Override + public void run() { + Plaintext msg = new Plaintext.Builder(BROADCAST) + .from(from) + .message(subject, message) + .build(); - LOG.info("Sending message."); - msg.setStatus(DOING_PROOF_OF_WORK); - ctx.getMessageRepository().save(msg); - ctx.send( - from, - from, - Factory.getBroadcast(from, msg), - +2 * DAY, - 0, - 0 - ); - msg.setStatus(SENT); - msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.BROADCAST, Label.Type.SENT)); - ctx.getMessageRepository().save(msg); + LOG.info("Sending message."); + msg.setStatus(DOING_PROOF_OF_WORK); + ctx.getMessageRepository().save(msg); + ctx.send( + from, + from, + Factory.getBroadcast(from, msg), + +2 * DAY, + 0, + 0 + ); + msg.setStatus(SENT); + msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.BROADCAST, Label.Type.SENT)); + ctx.getMessageRepository().save(msg); + } + }); } - public void send(BitmessageAddress from, BitmessageAddress to, String subject, String message) { + public void send(final BitmessageAddress from, final BitmessageAddress to, final String subject, final String message) { if (from.getPrivateKey() == null) { throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key."); } - // TODO: all this should happen in a separate thread - Plaintext msg = new Plaintext.Builder(MSG) - .from(from) - .to(to) - .message(subject, message) - .build(); - if (to.getPubkey() == null) { - tryToFindMatchingPubkey(to); - } - if (to.getPubkey() == null) { - LOG.info("Public key is missing from recipient. Requesting."); - requestPubkey(from, to); - msg.setStatus(PUBKEY_REQUESTED); - ctx.getMessageRepository().save(msg); - } else { - LOG.info("Sending message."); - msg.setStatus(DOING_PROOF_OF_WORK); - ctx.getMessageRepository().save(msg); - ctx.send( - from, - to, - new Msg(msg), - +2 * DAY, - ctx.getNonceTrialsPerByte(to), - ctx.getExtraBytes(to) - ); - msg.setStatus(SENT); - msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); - ctx.getMessageRepository().save(msg); - } + pool.submit(new Runnable() { + @Override + public void run() { + Plaintext msg = new Plaintext.Builder(MSG) + .from(from) + .to(to) + .message(subject, message) + .build(); + if (to.getPubkey() == null) { + tryToFindMatchingPubkey(to); + } + if (to.getPubkey() == null) { + LOG.info("Public key is missing from recipient. Requesting."); + requestPubkey(from, to); + msg.setStatus(PUBKEY_REQUESTED); + ctx.getMessageRepository().save(msg); + } else { + LOG.info("Sending message."); + msg.setStatus(DOING_PROOF_OF_WORK); + ctx.getMessageRepository().save(msg); + ctx.send( + from, + to, + new Msg(msg), + +2 * DAY, + ctx.getNonceTrialsPerByte(to), + ctx.getExtraBytes(to) + ); + msg.setStatus(SENT); + msg.addLabels(ctx.getMessageRepository().getLabels(Label.Type.SENT)); + ctx.getMessageRepository().save(msg); + } + } + }); } private void requestPubkey(BitmessageAddress requestingIdentity, BitmessageAddress address) { @@ -169,8 +187,7 @@ public class BitmessageContext { .expiresTime(expires) .payload(payload) .build(); - Security.doProofOfWork(object, ctx.getProofOfWorkEngine(), - ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); + ctx.getSecurity().doProofOfWork(object, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); ctx.getInventory().storeObject(object); ctx.getNetworkHandler().offer(object.getInventoryVector()); } @@ -184,6 +201,10 @@ public class BitmessageContext { ctx.getNetworkHandler().stop(); } + public boolean isRunning() { + return ctx.getNetworkHandler().isRunning(); + } + public void addContact(BitmessageAddress contact) { ctx.getAddressRepo().save(contact); tryToFindMatchingPubkey(contact); @@ -258,6 +279,7 @@ public class BitmessageContext { AddressRepository addressRepo; MessageRepository messageRepo; ProofOfWorkEngine proofOfWorkEngine; + Security security; public Builder() { } @@ -292,6 +314,11 @@ public class BitmessageContext { return this; } + public Builder security(Security security) { + this.security = security; + return this; + } + public Builder proofOfWorkEngine(ProofOfWorkEngine proofOfWorkEngine) { this.proofOfWorkEngine = proofOfWorkEngine; return this; diff --git a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java index 8ac64e0..03fef80 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/InternalContext.java @@ -21,7 +21,7 @@ import ch.dissem.bitmessage.entity.payload.Broadcast; import ch.dissem.bitmessage.entity.payload.GetPubkey; import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.ports.*; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.utils.Singleton; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,6 +42,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY; public class InternalContext { private final static Logger LOG = LoggerFactory.getLogger(InternalContext.class); + private final Security security; private final Inventory inventory; private final NodeRegistry nodeRegistry; private final NetworkHandler networkHandler; @@ -56,16 +57,29 @@ public class InternalContext { private long clientNonce; public InternalContext(BitmessageContext.Builder builder) { + this.security = builder.security; this.inventory = builder.inventory; this.nodeRegistry = builder.nodeRegistry; this.networkHandler = builder.networkHandler; this.addressRepository = builder.addressRepo; this.messageRepository = builder.messageRepo; this.proofOfWorkEngine = builder.proofOfWorkEngine; - this.clientNonce = Security.randomNonce(); + this.clientNonce = security.randomNonce(); + + Singleton.initialize(security); port = builder.port; - streams.add(1L); // FIXME + + // TODO: streams of new identities and subscriptions should also be added. This works only after a restart. + for (BitmessageAddress address : addressRepository.getIdentities()) { + streams.add(address.getStream()); + } + for (BitmessageAddress address : addressRepository.getSubscriptions()) { + streams.add(address.getStream()); + } + if (streams.isEmpty()) { + streams.add(1L); + } init(inventory, nodeRegistry, networkHandler, addressRepository, messageRepository, proofOfWorkEngine); } @@ -78,6 +92,10 @@ public class InternalContext { } } + public Security getSecurity() { + return security; + } + public Inventory getInventory() { return inventory; } @@ -111,14 +129,6 @@ public class InternalContext { return result; } - public void addStream(long stream) { - streams.add(stream); - } - - public void removeStream(long stream) { - streams.remove(stream); - } - public int getPort() { return port; } @@ -152,14 +162,14 @@ public class InternalContext { .payload(payload) .build(); if (object.isSigned()) { - object.sign(from.getPrivateKey()); + object.sign( from.getPrivateKey()); } if (payload instanceof Broadcast) { ((Broadcast) payload).encrypt(); } else if (payload instanceof Encrypted) { object.encrypt(to.getPubkey()); } - Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes); + security.doProofOfWork(object, nonceTrialsPerByte, extraBytes); if (payload instanceof PlaintextHolder) { Plaintext plaintext = ((PlaintextHolder) payload).getPlaintext(); plaintext.setInventoryVector(object.getInventoryVector()); @@ -182,13 +192,13 @@ public class InternalContext { .payload(identity.getPubkey()) .build(); response.sign(identity.getPrivateKey()); - response.encrypt(Security.createPublicKey(identity.getPublicDecryptionKey()).getEncoded(false)); - Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); + response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); + security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); if (response.isSigned()) { response.sign(identity.getPrivateKey()); } if (response instanceof Encrypted) { - response.encrypt(Security.createPublicKey(identity.getPublicDecryptionKey()).getEncoded(false)); + response.encrypt(security.createPublicKey(identity.getPublicDecryptionKey())); } inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); @@ -206,7 +216,7 @@ public class InternalContext { .expiresTime(expires) .payload(new GetPubkey(contact)) .build(); - Security.doProofOfWork(response, proofOfWorkEngine, networkNonceTrialsPerByte, networkExtraBytes); + security.doProofOfWork(response, networkNonceTrialsPerByte, networkExtraBytes); inventory.storeObject(response); networkHandler.offer(response.getInventoryVector()); } @@ -215,10 +225,6 @@ public class InternalContext { return clientNonce; } - public void setClientNonce(long clientNonce) { - this.clientNonce = clientNonce; - } - public interface ContextHolder { void setContext(InternalContext context); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java index eb348a2..0441e6e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/BitmessageAddress.java @@ -19,22 +19,27 @@ 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.utils.*; +import ch.dissem.bitmessage.utils.AccessCounter; +import ch.dissem.bitmessage.utils.Base58; +import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.Encode; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.Serializable; import java.util.Arrays; import java.util.Objects; import static ch.dissem.bitmessage.utils.Decode.bytes; import static ch.dissem.bitmessage.utils.Decode.varInt; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * A Bitmessage address. Can be a user's private address, an address string without public keys or a recipient's address * holding private keys. */ -public class BitmessageAddress { +public class BitmessageAddress implements Serializable { private final long version; private final long stream; private final byte[] ripe; @@ -62,19 +67,19 @@ public class BitmessageAddress { Encode.varInt(version, os); Encode.varInt(stream, os); if (version < 4) { - byte[] checksum = Security.sha512(os.toByteArray(), ripe); + byte[] checksum = security().sha512(os.toByteArray(), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { // for tag and decryption key, the checksum has to be created with 0x00 padding - byte[] checksum = Security.doubleSha512(os.toByteArray(), ripe); + byte[] checksum = security().doubleSha512(os.toByteArray(), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } // but for the address and its checksum they need to be stripped int offset = Bytes.numberOfLeadingZeros(ripe); os.write(ripe, offset, ripe.length - offset); - byte[] checksum = Security.doubleSha512(os.toByteArray()); + byte[] checksum = security().doubleSha512(os.toByteArray()); os.write(checksum, 0, 4); this.address = "BM-" + Base58.encode(os.toByteArray()); } catch (IOException e) { @@ -103,18 +108,18 @@ public class BitmessageAddress { this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20); // test checksum - byte[] checksum = Security.doubleSha512(bytes, bytes.length - 4); + byte[] checksum = security().doubleSha512(bytes, bytes.length - 4); byte[] expectedChecksum = bytes(in, 4); for (int i = 0; i < 4; i++) { if (expectedChecksum[i] != checksum[i]) throw new IllegalArgumentException("Checksum of address failed"); } if (version < 4) { - checksum = Security.sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); + checksum = security().sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = null; this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } else { - checksum = Security.doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); + checksum = security().doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe); this.tag = Arrays.copyOfRange(checksum, 32, 64); this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32); } @@ -129,7 +134,7 @@ public class BitmessageAddress { Encode.varInt(version, out); Encode.varInt(stream, out); out.write(ripe); - return Arrays.copyOfRange(Security.doubleSha512(out.toByteArray()), 32, 64); + return Arrays.copyOfRange(security().doubleSha512(out.toByteArray()), 32, 64); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java index 8b15371..9eaa2ab 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Encrypted.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.ports.Security; import java.io.IOException; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java index c10f942..8790d3a 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/NetworkMessage.java @@ -26,7 +26,7 @@ import java.security.GeneralSecurityException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import static ch.dissem.bitmessage.utils.Security.sha512; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * A network message is exchanged between two nodes. @@ -48,7 +48,7 @@ public class NetworkMessage implements Streamable { * First 4 bytes of sha512(payload) */ private byte[] getChecksum(byte[] bytes) throws NoSuchProviderException, NoSuchAlgorithmException { - byte[] d = sha512(bytes); + byte[] d = security().sha512(bytes); return new byte[]{d[0], d[1], d[2], d[3]}; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java index 0ef3340..128084e 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/ObjectMessage.java @@ -22,14 +22,16 @@ 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.ports.Security; import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Encode; -import ch.dissem.bitmessage.utils.Security; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** * The 'object' command sends an object that is shared throughout the network. */ @@ -89,7 +91,9 @@ public class ObjectMessage implements MessagePayload { } public InventoryVector getInventoryVector() { - return new InventoryVector(Bytes.truncate(Security.doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32)); + return new InventoryVector( + Bytes.truncate(security().doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32) + ); } private boolean isEncrypted() { @@ -113,7 +117,7 @@ public class ObjectMessage implements MessagePayload { public void sign(PrivateKey key) { if (payload.isSigned()) { - payload.setSignature(Security.getSignature(getBytesToSign(), key)); + payload.setSignature(security().getSignature(getBytesToSign(), key)); } } @@ -147,7 +151,7 @@ public class ObjectMessage implements MessagePayload { public boolean isSignatureValid(Pubkey pubkey) throws IOException { if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first"); - return Security.isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); + return security().isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey); } @Override diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java index 28cebfa..e73849f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -19,6 +19,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Encode; @@ -28,7 +29,7 @@ import java.util.*; /** * The unencrypted message to be sent by 'msg' or 'broadcast'. */ -public class Plaintext implements Streamable { +public class Plaintext implements Streamable, Serializable { private final Type type; private final BitmessageAddress from; private final long encoding; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java b/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java index 9ee4cf9..0601a44 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/Streamable.java @@ -16,6 +16,8 @@ package ch.dissem.bitmessage.entity; +import ch.dissem.bitmessage.ports.Security; + import java.io.IOException; import java.io.OutputStream; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java index 2491d85..bf5ff7f 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Broadcast.java @@ -21,11 +21,12 @@ import ch.dissem.bitmessage.entity.Encrypted; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.PlaintextHolder; import ch.dissem.bitmessage.exception.DecryptionFailedException; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.ports.Security; import java.io.IOException; import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * Users who are subscribed to the sending address will see the message appear in their inbox. @@ -78,7 +79,7 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted, Plai } public void encrypt() throws IOException { - encrypt(Security.createPublicKey(plaintext.getFrom().getPublicDecryptionKey()).getEncoded(false)); + encrypt(security().createPublicKey(plaintext.getFrom().getPublicDecryptionKey())); } @Override diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java index 2018373..7f994d1 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/CryptoBox.java @@ -17,28 +17,16 @@ package ch.dissem.bitmessage.entity.payload; import ch.dissem.bitmessage.entity.Streamable; -import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.DecryptionFailedException; import ch.dissem.bitmessage.utils.*; -import org.bouncycastle.crypto.BufferedBlockCipher; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.modes.CBCBlockCipher; -import org.bouncycastle.crypto.paddings.PKCS7Padding; -import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.math.ec.ECFieldElement; -import org.bouncycastle.math.ec.ECPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; -import java.math.BigInteger; import java.util.Arrays; import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE; +import static ch.dissem.bitmessage.utils.Singleton.security; public class CryptoBox implements Streamable { @@ -46,35 +34,31 @@ public class CryptoBox implements Streamable { private final byte[] initializationVector; private final int curveType; - private final ECPoint R; + private final byte[] R; private final byte[] mac; private byte[] encrypted; - public CryptoBox(Streamable data, byte[] encryptionKey) throws IOException { - this(data, Security.keyToPoint(encryptionKey)); - } - - public CryptoBox(Streamable data, ECPoint K) throws IOException { + public CryptoBox(Streamable data, byte[] K) throws IOException { curveType = 0x02CA; // 1. The destination public key is called K. // 2. Generate 16 random bytes using a secure random number generator. Call them IV. - initializationVector = Security.randomBytes(16); + initializationVector = security().randomBytes(16); // 3. Generate a new random EC key pair with private key called r and public key called R. - byte[] r = Security.randomBytes(PRIVATE_KEY_SIZE); - R = Security.createPublicKey(r); + byte[] r = security().randomBytes(PRIVATE_KEY_SIZE); + R = security().createPublicKey(r); // 4. Do an EC point multiply with public key K and private key r. This gives you public key P. - ECPoint P = K.multiply(Security.keyToBigInt(r)).normalize(); - byte[] X = P.getXCoord().getEncoded(); + byte[] P = security().multiply(K, r); + byte[] X = Points.getX(P); // 5. Use the X component of public key P and calculate the SHA512 hash H. - byte[] H = Security.sha512(X); + byte[] H = security().sha512(X); // 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); // 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7. // 8. Encrypt the data with AES-256-CBC, using IV as initialization vector, key_e as encryption key and the padded input text as payload. Call the output cipher text. - encrypted = crypt(true, Encode.bytes(data), key_e); + encrypted = security().crypt(true, Encode.bytes(data), key_e, initializationVector); // 9. Calculate a 32 byte MAC with HMACSHA256, using key_m as salt and IV + R + cipher text as data. Call the output MAC. mac = calculateMac(key_m); @@ -84,7 +68,7 @@ public class CryptoBox implements Streamable { private CryptoBox(Builder builder) { initializationVector = builder.initializationVector; curveType = builder.curveType; - R = Security.createPoint(builder.xComponent, builder.yComponent); + R = security().createPoint(builder.xComponent, builder.yComponent); encrypted = builder.encrypted; mac = builder.mac; } @@ -102,18 +86,17 @@ public class CryptoBox implements Streamable { } /** - * @param privateKey a private key, typically should be 32 bytes long + * @param k a private key, typically should be 32 bytes long * @return an InputStream yielding the decrypted data * @throws DecryptionFailedException if the payload can't be decrypted using this private key * @see https://bitmessage.org/wiki/Encryption#Decryption */ - public InputStream decrypt(byte[] privateKey) throws DecryptionFailedException { + public InputStream decrypt(byte[] k) 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. - ECPoint P = R.multiply(k).normalize(); + byte[] P = security().multiply(R, k); // 3. Use the X component of public key P and calculate the SHA512 hash H. - byte[] H = Security.sha512(P.getXCoord().getEncoded()); + byte[] H = security().sha512(Arrays.copyOfRange(P, 1, 33)); // 4. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m. byte[] key_e = Arrays.copyOfRange(H, 0, 32); byte[] key_m = Arrays.copyOfRange(H, 32, 64); @@ -126,49 +109,28 @@ public class CryptoBox implements Streamable { // 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key // and the cipher text as payload. The output is the padded input text. - return new ByteArrayInputStream(crypt(false, encrypted, key_e)); + return new ByteArrayInputStream(security().crypt(false, encrypted, key_e, initializationVector)); } private byte[] calculateMac(byte[] key_m) { try { ByteArrayOutputStream macData = new ByteArrayOutputStream(); writeWithoutMAC(macData); - return Security.mac(key_m, macData.toByteArray()); + return security().mac(key_m, macData.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); } } - private byte[] crypt(boolean encrypt, byte[] data, byte[] key_e) { - BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), new PKCS7Padding()); - - CipherParameters params = new ParametersWithIV(new KeyParameter(key_e), initializationVector); - - cipher.init(encrypt, params); - - byte[] buffer = new byte[cipher.getOutputSize(data.length)]; - int length = cipher.processBytes(data, 0, data.length, buffer, 0); - try { - length += cipher.doFinal(buffer, length); - } catch (InvalidCipherTextException e) { - throw new IllegalArgumentException(e); - } - if (length < buffer.length) { - return Arrays.copyOfRange(buffer, 0, length); - } - return buffer; - } - private void writeWithoutMAC(OutputStream out) throws IOException { out.write(initializationVector); Encode.int16(curveType, out); - writeCoordinateComponent(out, R.getXCoord()); - writeCoordinateComponent(out, R.getYCoord()); + writeCoordinateComponent(out, Points.getX(R)); + writeCoordinateComponent(out, Points.getY(R)); out.write(encrypted); } - private void writeCoordinateComponent(OutputStream out, ECFieldElement coord) throws IOException { - byte[] x = coord.getEncoded(); + private void writeCoordinateComponent(OutputStream out, byte[] x) throws IOException { int offset = Bytes.numberOfLeadingZeros(x); int length = x.length - offset; Encode.int16(length, out); diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java index 2bdcbb6..1243b32 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/Pubkey.java @@ -20,8 +20,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import static ch.dissem.bitmessage.utils.Security.ripemd160; -import static ch.dissem.bitmessage.utils.Security.sha512; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * Public keys for signing and encryption, the answer to a 'getpubkey' request. @@ -34,7 +33,7 @@ public abstract class Pubkey extends ObjectPayload { } public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) { - return ripemd160(sha512(publicSigningKey, publicEncryptionKey)); + return security().ripemd160(security().sha512(publicSigningKey, publicEncryptionKey)); } public abstract byte[] getSigningKey(); @@ -44,7 +43,7 @@ public abstract class Pubkey extends ObjectPayload { public abstract int getBehaviorBitfield(); public byte[] getRipe() { - return ripemd160(sha512(getSigningKey(), getEncryptionKey())); + return security().ripemd160(security().sha512(getSigningKey(), getEncryptionKey())); } public long getNonceTrialsPerByte() { diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java index 39127a8..03364d2 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/payload/V4Broadcast.java @@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.ports.Security; import java.io.IOException; import java.io.InputStream; diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java index 366d26b..f87dd13 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/InventoryVector.java @@ -21,9 +21,10 @@ import ch.dissem.bitmessage.utils.Strings; import java.io.IOException; import java.io.OutputStream; +import java.io.Serializable; import java.util.Arrays; -public class InventoryVector implements Streamable { +public class InventoryVector implements Streamable, Serializable { /** * Hash of the object */ diff --git a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java index f0956a6..d07c859 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java +++ b/domain/src/main/java/ch/dissem/bitmessage/entity/valueobject/PrivateKey.java @@ -18,18 +18,18 @@ package ch.dissem.bitmessage.entity.valueobject; import ch.dissem.bitmessage.entity.Streamable; import ch.dissem.bitmessage.entity.payload.Pubkey; -import ch.dissem.bitmessage.entity.payload.V3Pubkey; -import ch.dissem.bitmessage.entity.payload.V4Pubkey; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.utils.Bytes; import ch.dissem.bitmessage.utils.Decode; import ch.dissem.bitmessage.utils.Encode; -import ch.dissem.bitmessage.utils.Security; import java.io.*; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** - * Created by chris on 18.04.15. + * Represents a private key. Additional information (stream, version, features, ...) is stored in the accompanying + * {@link Pubkey} object. */ public class PrivateKey implements Streamable { public static final int PRIVATE_KEY_SIZE = 32; @@ -45,15 +45,15 @@ public class PrivateKey implements Streamable { byte[] pubEK; byte[] ripe; do { - privSK = Security.randomBytes(PRIVATE_KEY_SIZE); - privEK = Security.randomBytes(PRIVATE_KEY_SIZE); - pubSK = Security.createPublicKey(privSK).getEncoded(false); - pubEK = Security.createPublicKey(privEK).getEncoded(false); + privSK = security().randomBytes(PRIVATE_KEY_SIZE); + privEK = security().randomBytes(PRIVATE_KEY_SIZE); + pubSK = security().createPublicKey(privSK); + pubEK = security().createPublicKey(privEK); ripe = Pubkey.getRipe(pubSK, pubEK); } while (ripe[0] != 0 || (shorter && ripe[1] != 0)); this.privateSigningKey = privSK; this.privateEncryptionKey = privEK; - this.pubkey = Security.createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, + this.pubkey = security().createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } @@ -66,9 +66,9 @@ public class PrivateKey implements Streamable { public PrivateKey(long version, long stream, String passphrase, long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { try { // FIXME: this is most definitely wrong - this.privateSigningKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); - this.privateEncryptionKey = Bytes.truncate(Security.sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); - this.pubkey = Security.createPubkey(version, stream, privateSigningKey, privateEncryptionKey, + this.privateSigningKey = Bytes.truncate(security().sha512(passphrase.getBytes("UTF-8"), new byte[]{0}), 32); + this.privateEncryptionKey = Bytes.truncate(security().sha512(passphrase.getBytes("UTF-8"), new byte[]{1}), 32); + this.pubkey = security().createPubkey(version, stream, privateSigningKey, privateEncryptionKey, nonceTrialsPerByte, extraBytes, features); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java index a144bb6..5584ec7 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/Factory.java @@ -23,7 +23,7 @@ import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.payload.*; import ch.dissem.bitmessage.entity.valueobject.PrivateKey; import ch.dissem.bitmessage.exception.NodeException; -import ch.dissem.bitmessage.utils.Security; +import ch.dissem.bitmessage.ports.Security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +32,8 @@ import java.io.InputStream; import java.net.SocketException; import java.net.SocketTimeoutException; +import static ch.dissem.bitmessage.utils.Singleton.security; + /** * Creates {@link NetworkMessage} objects from {@link InputStream InputStreams} */ @@ -115,8 +117,8 @@ public class Factory { BitmessageAddress temp = new BitmessageAddress(address); PrivateKey privateKey = new PrivateKey(privateSigningKey, privateEncryptionKey, createPubkey(temp.getVersion(), temp.getStream(), - Security.createPublicKey(privateSigningKey).getEncoded(false), - Security.createPublicKey(privateEncryptionKey).getEncoded(false), + security().createPublicKey(privateSigningKey), + security().createPublicKey(privateEncryptionKey), nonceTrialsPerByte, extraBytes, behaviourBitfield)); BitmessageAddress result = new BitmessageAddress(privateKey); if (!result.getAddress().equals(address)) { @@ -126,11 +128,17 @@ public class Factory { return result; } - public static BitmessageAddress generatePrivateAddress(boolean shorter, long stream, Pubkey.Feature... features) { + public static BitmessageAddress generatePrivateAddress(boolean shorter, + long stream, + Pubkey.Feature... features) { return new BitmessageAddress(new PrivateKey(shorter, stream, 1000, 1000, features)); } - static ObjectPayload getObjectPayload(long objectType, long version, long streamNumber, InputStream stream, int length) throws IOException { + static ObjectPayload getObjectPayload(long objectType, + long version, + long streamNumber, + InputStream stream, + int length) throws IOException { ObjectType type = ObjectType.fromNumber(objectType); if (type != null) { switch (type) { diff --git a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java index 57d240d..7e48746 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java +++ b/domain/src/main/java/ch/dissem/bitmessage/factory/V3MessageFactory.java @@ -22,9 +22,9 @@ import ch.dissem.bitmessage.entity.payload.ObjectPayload; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; import ch.dissem.bitmessage.exception.NodeException; +import ch.dissem.bitmessage.ports.Security; import ch.dissem.bitmessage.utils.AccessCounter; import ch.dissem.bitmessage.utils.Decode; -import ch.dissem.bitmessage.utils.Security; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import static ch.dissem.bitmessage.entity.NetworkMessage.MAGIC_BYTES; +import static ch.dissem.bitmessage.utils.Singleton.security; /** * Creates protocol v3 network messages from {@link InputStream InputStreams} @@ -174,7 +175,7 @@ class V3MessageFactory { } private static boolean testChecksum(byte[] checksum, byte[] payload) { - byte[] payloadChecksum = Security.sha512(payload); + byte[] payloadChecksum = security().sha512(payload); for (int i = 0; i < checksum.length; i++) { if (checksum[i] != payloadChecksum[i]) { return false; diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java new file mode 100644 index 0000000..00ed1f2 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/AbstractSecurity.java @@ -0,0 +1,175 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.ports; + +import ch.dissem.bitmessage.InternalContext; +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; +import ch.dissem.bitmessage.factory.Factory; +import ch.dissem.bitmessage.utils.Bytes; +import ch.dissem.bitmessage.utils.UnixTime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.SecureRandom; + +/** + * Implements everything that isn't directly dependent on either Spongy- or Bouncycastle. + */ +public abstract class AbstractSecurity implements Security, InternalContext.ContextHolder { + public static final Logger LOG = LoggerFactory.getLogger(Security.class); + private static final SecureRandom RANDOM = new SecureRandom(); + private static final BigInteger TWO = BigInteger.valueOf(2); + + private final String provider; + private InternalContext context; + + protected AbstractSecurity(String provider) { + this.provider = provider; + } + + @Override + public void setContext(InternalContext context) { + this.context = context; + } + + public byte[] sha512(byte[]... data) { + return hash("SHA-512", data); + } + + public byte[] doubleSha512(byte[]... data) { + MessageDigest mda = md("SHA-512"); + for (byte[] d : data) { + mda.update(d); + } + return mda.digest(mda.digest()); + } + + public byte[] doubleSha512(byte[] data, int length) { + MessageDigest mda = md("SHA-512"); + mda.update(data, 0, length); + return mda.digest(mda.digest()); + } + + public byte[] ripemd160(byte[]... data) { + return hash("RIPEMD160", data); + } + + public byte[] doubleSha256(byte[] data, int length) { + MessageDigest mda = md("SHA-256"); + mda.update(data, 0, length); + return mda.digest(mda.digest()); + } + + public byte[] sha1(byte[]... data) { + return hash("SHA-1", data); + } + + public byte[] randomBytes(int length) { + byte[] result = new byte[length]; + RANDOM.nextBytes(result); + return result; + } + + public void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, + long extraBytes) { + try { + if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; + if (extraBytes < 1000) extraBytes = 1000; + + byte[] initialHash = getInitialHash(object); + + byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); + + byte[] nonce = context.getProofOfWorkEngine().calculateNonce(initialHash, target); + object.setNonce(nonce); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) + throws IOException { + byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); + byte[] value = doubleSha512(object.getNonce(), getInitialHash(object)); + if (Bytes.lt(target, value, 8)) { + throw new InsufficientProofOfWorkException(target, value); + } + } + + private byte[] getInitialHash(ObjectMessage object) throws IOException { + return sha512(object.getPayloadBytesWithoutNonce()); + } + + private byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { + BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now()); + LOG.debug("TTL: " + TTL + "s"); + BigInteger numerator = TWO.pow(64); + BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes); + BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte).multiply(powLength.add(powLength.multiply(TTL).divide(BigInteger.valueOf(2).pow(16)))); + return Bytes.expand(numerator.divide(denominator).toByteArray(), 8); + } + + private byte[] hash(String algorithm, byte[]... data) { + MessageDigest mda = md(algorithm); + for (byte[] d : data) { + mda.update(d); + } + return mda.digest(); + } + + private MessageDigest md(String algorithm) { + try { + return MessageDigest.getInstance(algorithm, provider); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + public byte[] mac(byte[] key_m, byte[] data) { + try { + Mac mac = Mac.getInstance("HmacSHA256", provider); + mac.init(new SecretKeySpec(key_m, "HmacSHA256")); + return mac.doFinal(data); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + public Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, + long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { + return Factory.createPubkey(version, stream, + createPublicKey(privateSigningKey), + createPublicKey(privateEncryptionKey), + nonceTrialsPerByte, extraBytes, features); + } + + public BigInteger keyToBigInt(byte[] privateKey) { + return new BigInteger(1, privateKey); + } + + public long randomNonce() { + return RANDOM.nextLong(); + } +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java index ed2f38a..4b73629 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/NetworkHandler.java @@ -34,6 +34,8 @@ public interface NetworkHandler { Property getNetworkStatus(); + boolean isRunning(); + interface MessageListener { void receive(ObjectMessage object) throws IOException; } diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java new file mode 100644 index 0000000..83ad43d --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/Security.java @@ -0,0 +1,206 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.ports; + +import ch.dissem.bitmessage.entity.ObjectMessage; +import ch.dissem.bitmessage.entity.payload.Pubkey; +import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.SecureRandom; + +/** + * Provides some methods to help with hashing and encryption. All randoms are created using {@link SecureRandom}, + * which should be secure enough. + */ +public interface Security { + /** + * A helper method to calculate SHA-512 hashes. Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in + * success on the same thread. + * + * @param data to get hashed + * @return SHA-512 hash of data + */ + byte[] sha512(byte[]... data); + + /** + * A helper method to calculate doubleSHA-512 hashes. Please note that a new {@link MessageDigest} object is created + * at each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in + * success on the same thread. + * + * @param data to get hashed + * @return SHA-512 hash of data + */ + byte[] doubleSha512(byte[]... data); + + /** + * A helper method to calculate double SHA-512 hashes. This method allows to only use a part of the available bytes + * to use for the hash calculation. + *
+ * Please note that a new {@link MessageDigest} object is created at each call (to ensure thread safety), so you + * shouldn't use this if you need to do many hash calculations in short order on the same thread. + *
+ * + * @param data to get hashed + * @param length number of bytes to be taken into account + * @return SHA-512 hash of data + */ + byte[] doubleSha512(byte[] data, int length); + + /** + * A helper method to calculate RIPEMD-160 hashes. Supplying multiple byte arrays has the same result as a + * concatenation of all arrays, but might perform better. + *+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *
+ * + * @param data to get hashed + * @return RIPEMD-160 hash of data + */ + byte[] ripemd160(byte[]... data); + + /** + * A helper method to calculate double SHA-256 hashes. This method allows to only use a part of the available bytes + * to use for the hash calculation. + *+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *
+ * + * @param data to get hashed + * @param length number of bytes to be taken into account + * @return SHA-256 hash of data + */ + byte[] doubleSha256(byte[] data, int length); + + /** + * A helper method to calculate SHA-1 hashes. Supplying multiple byte arrays has the same result as a + * concatenation of all arrays, but might perform better. + *+ * Please note that a new {@link MessageDigest} object is created at + * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short + * order on the same thread. + *
+ * + * @param data to get hashed + * @return SHA hash of data + */ + byte[] sha1(byte[]... data); + + /** + * @param length number of bytes to return + * @return an array of the given size containing random bytes + */ + byte[] randomBytes(int length); + + /** + * Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to + * live. + * + * @param object to do the proof of work for + * @param nonceTrialsPerByte difficulty + * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) + */ + void doProofOfWork(ObjectMessage object, long nonceTrialsPerByte, + long extraBytes); + + /** + * @param object to be checked + * @param nonceTrialsPerByte difficulty + * @param extraBytes bytes to add to the object size + * @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages) + */ + void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) + throws IOException; + + /** + * Calculates the MAC for a message (data) + * + * @param key_m the symmetric key used + * @param data the message data to calculate the MAC for + * @return the MAC + */ + byte[] mac(byte[] key_m, byte[] data); + + /** + * + * @param encrypt if true, encrypts data, otherwise tries to decrypt it. + * @param data + * @param key_e + * @return + */ + byte[] crypt(boolean encrypt, byte[] data, byte[] key_e, byte[] initializationVector); + + /** + * Create a new public key fom given private keys. + * + * @param version of the public key / address + * @param stream of the address + * @param privateSigningKey private key used for signing + * @param privateEncryptionKey private key used for encryption + * @param nonceTrialsPerByte proof of work difficulty + * @param extraBytes bytes to add for the proof of work (make it harder for small messages) + * @param features of the address + * @return a public key object + */ + Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, + long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features); + + /** + * @param privateKey private key as byte array + * @return a public key corresponding to the given private key + */ + byte[] createPublicKey(byte[] privateKey); + + /** + * @param privateKey private key as byte array + * @return a big integer representation (unsigned) of the given bytes + */ + BigInteger keyToBigInt(byte[] privateKey); + + /** + * @param data to check + * @param signature the signature of the message + * @param pubkey the sender's public key + * @return true if the signature is valid, false otherwise + */ + boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey); + + /** + * Calculate the signature of data, using the given private key. + * + * @param data to be signed + * @param privateKey to be used for signing + * @return the signature + */ + byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey); + + /** + * @return a random number of type long + */ + long randomNonce(); + + byte[] multiply(byte[] k, byte[] r); + + byte[] createPoint(byte[] x, byte[] y); +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java index 0d9d392..211df53 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java +++ b/domain/src/main/java/ch/dissem/bitmessage/ports/SimplePOWEngine.java @@ -23,7 +23,7 @@ import java.security.MessageDigest; import static ch.dissem.bitmessage.utils.Bytes.inc; /** - * Created by chris on 14.04.15. + * You should really use the MultiThreadedPOWEngine, but this one might help you grok the other one. */ public class SimplePOWEngine implements ProofOfWorkEngine { @Override diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java new file mode 100644 index 0000000..937fe20 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Points.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.utils; + +import java.util.Arrays; + +/** + * Created by chris on 20.07.15. + */ +public class Points { + public static byte[] getX(byte[] P) { + return Arrays.copyOfRange(P, 1, ((P.length - 1) / 2) + 1); + } + + public static byte[] getY(byte[] P) { + return Arrays.copyOfRange(P, ((P.length - 1) / 2) + 1, P.length); + } +} diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java index 1030513..6fa0ec4 100644 --- a/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Property.java @@ -17,7 +17,12 @@ package ch.dissem.bitmessage.utils; /** - * Created by chris on 14.06.15. + * Some property that has a name, a value and/or other properties. This can be used for any purpose, but is for now + * used to contain different status information. It is by default displayed in some JSON inspired human readable + * notation, but you might only want to rely on the 'human readable' part. + *
+ * If you need a real JSON representation, please add a method toJson()
.
+ *
- * Please note that a new {@link MessageDigest} object is created at each call (to ensure thread safety), so you - * shouldn't use this if you need to do many hash calculations in short order on the same thread. - *
- * - * @param data to get hashed - * @param length number of bytes to be taken into account - * @return SHA-512 hash of data - */ - public static byte[] doubleSha512(byte[] data, int length) { - MessageDigest mda = md("SHA-512"); - mda.update(data, 0, length); - return mda.digest(mda.digest()); - } - - - /** - * A helper method to calculate RIPEMD-160 hashes. Supplying multiple byte arrays has the same result as a - * concatenation of all arrays, but might perform better. - *- * Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short - * order on the same thread. - *
- * - * @param data to get hashed - * @return RIPEMD-160 hash of data - */ - public static byte[] ripemd160(byte[]... data) { - return hash("RIPEMD160", data); - } - - /** - * A helper method to calculate double SHA-256 hashes. This method allows to only use a part of the available bytes - * to use for the hash calculation. - *- * Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short - * order on the same thread. - *
- * - * @param data to get hashed - * @param length number of bytes to be taken into account - * @return SHA-256 hash of data - */ - public static byte[] doubleSha256(byte[] data, int length) { - MessageDigest mda = md("SHA-256"); - mda.update(data, 0, length); - return mda.digest(mda.digest()); - } - - /** - * A helper method to calculate SHA-1 hashes. Supplying multiple byte arrays has the same result as a - * concatenation of all arrays, but might perform better. - *- * Please note that a new {@link MessageDigest} object is created at - * each call (to ensure thread safety), so you shouldn't use this if you need to do many hash calculations in short - * order on the same thread. - *
- * - * @param data to get hashed - * @return SHA hash of data - */ - public static byte[] sha1(byte[]... data) { - return hash("SHA-1", data); - } - - /** - * @param length number of bytes to return - * @return an array of the given size containing random bytes - */ - public static byte[] randomBytes(int length) { - byte[] result = new byte[length]; - RANDOM.nextBytes(result); - return result; - } - - /** - * Calculates the proof of work. This might take a long time, depending on the hardware, message size and time to - * live. - * - * @param object to do the proof of work for - * @param worker doing the actual proof of work - * @param nonceTrialsPerByte difficulty - * @param extraBytes bytes to add to the object size (makes it more difficult to send small messages) - */ - public static void doProofOfWork(ObjectMessage object, ProofOfWorkEngine worker, long nonceTrialsPerByte, - long extraBytes) { - try { - if (nonceTrialsPerByte < 1000) nonceTrialsPerByte = 1000; - if (extraBytes < 1000) extraBytes = 1000; - - byte[] initialHash = getInitialHash(object); - - byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); - - byte[] nonce = worker.calculateNonce(initialHash, target); - object.setNonce(nonce); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * @param object to be checked - * @param nonceTrialsPerByte difficulty - * @param extraBytes bytes to add to the object size - * @throws InsufficientProofOfWorkException if proof of work doesn't check out (makes it more difficult to send small messages) - */ - public static void checkProofOfWork(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) - throws IOException { - byte[] target = getProofOfWorkTarget(object, nonceTrialsPerByte, extraBytes); - byte[] value = Security.doubleSha512(object.getNonce(), getInitialHash(object)); - if (Bytes.lt(target, value, 8)) { - throw new InsufficientProofOfWorkException(target, value); - } - } - - private static byte[] getInitialHash(ObjectMessage object) throws IOException { - return Security.sha512(object.getPayloadBytesWithoutNonce()); - } - - private static byte[] getProofOfWorkTarget(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) throws IOException { - BigInteger TTL = BigInteger.valueOf(object.getExpiresTime() - UnixTime.now()); - LOG.debug("TTL: " + TTL + "s"); - BigInteger numerator = TWO.pow(64); - BigInteger powLength = BigInteger.valueOf(object.getPayloadBytesWithoutNonce().length + extraBytes); - BigInteger denominator = BigInteger.valueOf(nonceTrialsPerByte).multiply(powLength.add(powLength.multiply(TTL).divide(BigInteger.valueOf(2).pow(16)))); - return Bytes.expand(numerator.divide(denominator).toByteArray(), 8); - } - - private static byte[] hash(String algorithm, byte[]... data) { - MessageDigest mda = md(algorithm); - for (byte[] d : data) { - mda.update(d); - } - return mda.digest(); - } - - private static MessageDigest md(String algorithm) { - try { - return MessageDigest.getInstance(algorithm, "BC"); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - /** - * Calculates the MAC for a message (data) - * - * @param key_m the symmetric key used - * @param data the message data to calculate the MAC for - * @return the MAC - */ - public static byte[] mac(byte[] key_m, byte[] data) { - try { - Mac mac = Mac.getInstance("HmacSHA256", "BC"); - mac.init(new SecretKeySpec(key_m, "HmacSHA256")); - return mac.doFinal(data); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - /** - * Create a new public key fom given private keys. - * - * @param version of the public key / address - * @param stream of the address - * @param privateSigningKey private key used for signing - * @param privateEncryptionKey private key used for encryption - * @param nonceTrialsPerByte proof of work difficulty - * @param extraBytes bytes to add for the proof of work (make it harder for small messages) - * @param features of the address - * @return a public key object - */ - public static Pubkey createPubkey(long version, long stream, byte[] privateSigningKey, byte[] privateEncryptionKey, - long nonceTrialsPerByte, long extraBytes, Pubkey.Feature... features) { - return Factory.createPubkey(version, stream, - createPublicKey(privateSigningKey).getEncoded(false), - createPublicKey(privateEncryptionKey).getEncoded(false), - nonceTrialsPerByte, extraBytes, features); - } - - /** - * @param privateKey private key as byte array - * @return a public key corresponding to the given private key - */ - public static ECPoint createPublicKey(byte[] privateKey) { - return EC_CURVE_PARAMETERS.getG().multiply(keyToBigInt(privateKey)).normalize(); - } - - /** - * @param privateKey private key as byte array - * @return a big integer representation (unsigned) of the given bytes - */ - public static BigInteger keyToBigInt(byte[] privateKey) { - return new BigInteger(1, privateKey); - } - - public static ECPoint keyToPoint(byte[] publicKey) { - BigInteger x = new BigInteger(1, Arrays.copyOfRange(publicKey, 1, 33)); - BigInteger y = new BigInteger(1, Arrays.copyOfRange(publicKey, 33, 65)); - return EC_CURVE_PARAMETERS.getCurve().createPoint(x, y); - } - - public static ECPoint createPoint(byte[] x, byte[] y) { - return EC_CURVE_PARAMETERS.getCurve().createPoint( - new BigInteger(1, x), - new BigInteger(1, y) - ); - } - - /** - * @param data to check - * @param signature the signature of the message - * @param pubkey the sender's public key - * @return true if the signature is valid, false otherwise - */ - public static boolean isSignatureValid(byte[] data, byte[] signature, Pubkey pubkey) { - try { - ECParameterSpec spec = new ECParameterSpec( - EC_CURVE_PARAMETERS.getCurve(), - EC_CURVE_PARAMETERS.getG(), - EC_CURVE_PARAMETERS.getN(), - EC_CURVE_PARAMETERS.getH(), - EC_CURVE_PARAMETERS.getSeed() - ); - - ECPoint Q = keyToPoint(pubkey.getSigningKey()); - KeySpec keySpec = new ECPublicKeySpec(Q, spec); - PublicKey publicKey = KeyFactory.getInstance("ECDSA", "BC").generatePublic(keySpec); - - Signature sig = Signature.getInstance("ECDSA", "BC"); - sig.initVerify(publicKey); - sig.update(data); - return sig.verify(signature); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * Calculate the signature of data, using the given private key. - * - * @param data to be signed - * @param privateKey to be used for signing - * @return the signature - */ - public static byte[] getSignature(byte[] data, ch.dissem.bitmessage.entity.valueobject.PrivateKey privateKey) { - try { - ECParameterSpec spec = new ECParameterSpec( - EC_CURVE_PARAMETERS.getCurve(), - EC_CURVE_PARAMETERS.getG(), - EC_CURVE_PARAMETERS.getN(), - EC_CURVE_PARAMETERS.getH(), - EC_CURVE_PARAMETERS.getSeed() - ); - - BigInteger d = keyToBigInt(privateKey.getPrivateSigningKey()); - KeySpec keySpec = new ECPrivateKeySpec(d, spec); - PrivateKey privKey = KeyFactory.getInstance("ECDSA", "BC").generatePrivate(keySpec); - - Signature sig = Signature.getInstance("ECDSA", "BC"); - sig.initSign(privKey); - sig.update(data); - return sig.sign(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * @return a random number of type long - */ - public static long randomNonce() { - return RANDOM.nextLong(); - } -} diff --git a/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java new file mode 100644 index 0000000..169db77 --- /dev/null +++ b/domain/src/main/java/ch/dissem/bitmessage/utils/Singleton.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.utils; + +import ch.dissem.bitmessage.ports.Security; + +/** + * Created by chris on 20.07.15. + */ +public class Singleton { + private static Security security; + + public static void initialize(Security security) { + Singleton.security = security; + } + + public static Security security() { + return security; + } +} diff --git a/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java index ebb3707..21f9506 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/DecryptionTest.java @@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.payload.V4Broadcast; import ch.dissem.bitmessage.entity.payload.V5Broadcast; import ch.dissem.bitmessage.exception.DecryptionFailedException; +import ch.dissem.bitmessage.utils.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; @@ -29,7 +30,7 @@ import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -public class DecryptionTest { +public class DecryptionTest extends TestBase { @Test public void ensureV4BroadcastIsDecryptedCorrectly() throws IOException, DecryptionFailedException { BitmessageAddress address = new BitmessageAddress("BM-2D9Vc5rFxxR5vTi53T9gkLfemViHRMVLQZ"); diff --git a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java index f0f8ddb..9a24842 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/EncryptionTest.java @@ -24,20 +24,20 @@ 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.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; import java.io.IOException; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -public class EncryptionTest { +public class EncryptionTest extends TestBase { @Test public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException { - GenericPayload before = new GenericPayload(0, 1, Security.randomBytes(100)); + GenericPayload before = new GenericPayload(0, 1, security().randomBytes(100)); PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000); CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey()); diff --git a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java index dc2eb85..71b7d2a 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/SignatureTest.java @@ -25,6 +25,7 @@ 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.TestBase; import ch.dissem.bitmessage.utils.TestUtils; import org.junit.Test; @@ -33,7 +34,7 @@ import java.util.Date; import static org.junit.Assert.*; -public class SignatureTest { +public class SignatureTest extends TestBase { @Test public void ensureValidationWorks() throws IOException { ObjectMessage object = TestUtils.loadObjectMessage(3, "V3Pubkey.payload"); diff --git a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java index 85b8098..e1fcc7f 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/entity/BitmessageAddressTest.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.Arrays; import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.*; public class BitmessageAddressTest { @@ -102,7 +103,7 @@ public class BitmessageAddressTest { System.out.println("\n\n" + Strings.hex(privsigningkey) + "\n\n"); BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey, - Security.createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000))); + security().createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000))); assertEquals(address_string, address.getAddress()); } @@ -119,7 +120,7 @@ public class BitmessageAddressTest { if (bytes.length != 37) throw new IOException("Unknown format: 37 bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long"); - byte[] hash = Security.doubleSha256(bytes, 33); + byte[] hash = security().doubleSha256(bytes, 33); for (int i = 0; i < 4; i++) { if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat); } @@ -132,7 +133,7 @@ public class BitmessageAddressTest { byte[] privsigningkey = getSecret("5KMWqfCyJZGFgW6QrnPJ6L9Gatz25B51y7ErgqNr1nXUVbtZbdU"); byte[] privencryptionkey = getSecret("5JXXWEuhHQEPk414SzEZk1PHDRi8kCuZd895J7EnKeQSahJPxGz"); BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey, - Security.createPubkey(4, 1, privsigningkey, privencryptionkey, 320, 14000))); + security().createPubkey(4, 1, privsigningkey, privencryptionkey, 320, 14000))); assertEquals("BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke", address.getAddress()); } diff --git a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java index aed5722..ca17a23 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/ports/ProofOfWorkEngineTest.java @@ -17,14 +17,11 @@ package ch.dissem.bitmessage.ports; import ch.dissem.bitmessage.utils.Bytes; -import ch.dissem.bitmessage.utils.Security; import org.junit.Test; +import static ch.dissem.bitmessage.utils.Singleton.security; import static org.junit.Assert.assertTrue; -/** - * Created by chris on 17.04.15. - */ public class ProofOfWorkEngineTest { @Test public void testSimplePOWEngine() { @@ -38,11 +35,11 @@ public class ProofOfWorkEngineTest { private void testPOW(ProofOfWorkEngine engine) { long time = System.currentTimeMillis(); - byte[] initialHash = Security.sha512(new byte[]{1, 3, 6, 4}); + byte[] initialHash = security().sha512(new byte[]{1, 3, 6, 4}); byte[] target = {0, 0, -1, -1, -1, -1, -1, -1}; byte[] nonce = engine.calculateNonce(initialHash, target); System.out.println("Calculating nonce took " + (System.currentTimeMillis() - time) + "ms"); - assertTrue(Bytes.lt(Security.doubleSha512(nonce, initialHash), target, 8)); + assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8)); } } diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java index 7c83724..52eb106 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/BytesTest.java @@ -25,9 +25,6 @@ import java.util.Random; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -/** - * Created by chris on 10.04.15. - */ public class BytesTest { public static final Random rnd = new Random(); diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java index 8c18ee7..60d882f 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/DecodeTest.java @@ -22,9 +22,6 @@ import java.io.*; import static org.junit.Assert.assertEquals; -/** - * Created by chris on 20.03.15. - */ public class DecodeTest { @Test public void ensureDecodingWorks() throws Exception { diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java b/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java index 3489112..aaba5bf 100644 --- a/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/EncodeTest.java @@ -23,9 +23,6 @@ import java.io.IOException; import static org.junit.Assert.assertEquals; -/** - * Created by chris on 13.03.15. - */ public class EncodeTest { @Test public void testUint8() throws IOException { diff --git a/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java b/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java new file mode 100644 index 0000000..1dd1335 --- /dev/null +++ b/domain/src/test/java/ch/dissem/bitmessage/utils/TestBase.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.utils; + +import ch.dissem.bitmessage.security.bc.BouncySecurity; + +/** + * Created by chris on 20.07.15. + */ +public class TestBase { + static { + Singleton.initialize(new BouncySecurity()); + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f2e98f0..66e6c70 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java index 0eb0f09..d8054ff 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/Connection.java @@ -26,7 +26,6 @@ import ch.dissem.bitmessage.exception.NodeException; import ch.dissem.bitmessage.factory.Factory; import ch.dissem.bitmessage.ports.NetworkHandler.MessageListener; import ch.dissem.bitmessage.utils.DebugUtils; -import ch.dissem.bitmessage.utils.Security; import ch.dissem.bitmessage.utils.UnixTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,6 +43,7 @@ import java.util.concurrent.ConcurrentMap; import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; import static ch.dissem.bitmessage.networking.Connection.State.*; +import static ch.dissem.bitmessage.utils.Singleton.security; import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; /** @@ -275,7 +275,7 @@ public class Connection implements Runnable { ObjectMessage objectMessage = (ObjectMessage) messagePayload; try { LOG.debug("Received object " + objectMessage.getInventoryVector()); - Security.checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); + security().checkProofOfWork(objectMessage, ctx.getNetworkNonceTrialsPerByte(), ctx.getNetworkExtraBytes()); listener.receive(objectMessage); ctx.getInventory().storeObject(objectMessage); // offer object to some random nodes so it gets distributed throughout the network: diff --git a/networking/src/main/java/ch/dissem/bitmessage/networking/NetworkNode.java b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java similarity index 96% rename from networking/src/main/java/ch/dissem/bitmessage/networking/NetworkNode.java rename to networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java index f27f7ff..558d097 100644 --- a/networking/src/main/java/ch/dissem/bitmessage/networking/NetworkNode.java +++ b/networking/src/main/java/ch/dissem/bitmessage/networking/DefaultNetworkHandler.java @@ -44,9 +44,9 @@ import static ch.dissem.bitmessage.utils.DebugUtils.inc; /** * Handles all the networky stuff. */ -public class NetworkNode implements NetworkHandler, ContextHolder { +public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { public final static int NETWORK_MAGIC_NUMBER = 8; - private final static Logger LOG = LoggerFactory.getLogger(NetworkNode.class); + private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class); private final ExecutorService pool; private final List