Fixed system test and ProofOfWorkService
This commit is contained in:
parent
c7594795f0
commit
678a48ac3f
@ -18,12 +18,9 @@ package ch.dissem.bitmessage;
|
|||||||
|
|
||||||
import ch.dissem.bitmessage.entity.*;
|
import ch.dissem.bitmessage.entity.*;
|
||||||
import ch.dissem.bitmessage.entity.payload.Broadcast;
|
import ch.dissem.bitmessage.entity.payload.Broadcast;
|
||||||
import ch.dissem.bitmessage.entity.payload.Msg;
|
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
|
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
|
||||||
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||||
import ch.dissem.bitmessage.factory.Factory;
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
import ch.dissem.bitmessage.ports.*;
|
import ch.dissem.bitmessage.ports.*;
|
||||||
@ -178,24 +175,17 @@ public class BitmessageContext {
|
|||||||
if (to == null || to.getPubkey() != null) {
|
if (to == null || to.getPubkey() != null) {
|
||||||
LOG.info("Sending message.");
|
LOG.info("Sending message.");
|
||||||
ctx.getMessageRepository().save(msg);
|
ctx.getMessageRepository().save(msg);
|
||||||
|
if (msg.getType() == MSG) {
|
||||||
|
ctx.send(msg, TTL.msg());
|
||||||
|
} else {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
msg.getFrom(),
|
msg.getFrom(),
|
||||||
to,
|
to,
|
||||||
wrapInObjectPayload(msg),
|
Factory.getBroadcast(msg),
|
||||||
TTL.msg()
|
TTL.msg()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ObjectPayload wrapInObjectPayload(Plaintext msg) {
|
|
||||||
switch (msg.getType()) {
|
|
||||||
case MSG:
|
|
||||||
return new Msg(msg);
|
|
||||||
case BROADCAST:
|
|
||||||
return Factory.getBroadcast(msg);
|
|
||||||
default:
|
|
||||||
throw new ApplicationException("Unknown message type " + msg.getType());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startup() {
|
public void startup() {
|
||||||
|
@ -111,12 +111,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
|
|||||||
for (Plaintext msg : messages) {
|
for (Plaintext msg : messages) {
|
||||||
ctx.getLabeler().markAsSending(msg);
|
ctx.getLabeler().markAsSending(msg);
|
||||||
ctx.getMessageRepository().save(msg);
|
ctx.getMessageRepository().save(msg);
|
||||||
ctx.send(
|
ctx.send(msg, +2 * DAY);
|
||||||
msg.getFrom(),
|
|
||||||
msg.getTo(),
|
|
||||||
new Msg(msg),
|
|
||||||
+2 * DAY
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,6 +160,15 @@ public class InternalContext {
|
|||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void send(final Plaintext plaintext, final long timeToLive) {
|
||||||
|
if (plaintext.getAckMessage() != null) {
|
||||||
|
long expires = UnixTime.now(+timeToLive);
|
||||||
|
proofOfWorkService.doProofOfWorkWithAck(plaintext, expires);
|
||||||
|
} else {
|
||||||
|
send(plaintext.getFrom(), plaintext.getTo(), new Msg(plaintext), timeToLive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void send(final BitmessageAddress from, BitmessageAddress to, final ObjectPayload payload,
|
public void send(final BitmessageAddress from, BitmessageAddress to, final ObjectPayload payload,
|
||||||
final long timeToLive) {
|
final long timeToLive) {
|
||||||
try {
|
try {
|
||||||
@ -174,26 +183,12 @@ public class InternalContext {
|
|||||||
if (object.isSigned()) {
|
if (object.isSigned()) {
|
||||||
object.sign(from.getPrivateKey());
|
object.sign(from.getPrivateKey());
|
||||||
}
|
}
|
||||||
if (payload instanceof Msg && recipient.has(Pubkey.Feature.DOES_ACK)) {
|
|
||||||
final ObjectMessage ackMessage = ((Msg) payload).getPlaintext().getAckMessage();
|
|
||||||
cryptography.doProofOfWork(ackMessage, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES, new ProofOfWorkEngine.Callback() {
|
|
||||||
@Override
|
|
||||||
public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
|
|
||||||
// FIXME: the message gets lost if calculation is cancelled
|
|
||||||
// (e.g. by terminating the application)
|
|
||||||
ackMessage.setNonce(nonce);
|
|
||||||
object.encrypt(recipient.getPubkey());
|
|
||||||
proofOfWorkService.doProofOfWork(recipient, object);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (payload instanceof Broadcast) {
|
if (payload instanceof Broadcast) {
|
||||||
((Broadcast) payload).encrypt();
|
((Broadcast) payload).encrypt();
|
||||||
} else if (payload instanceof Encrypted) {
|
} else if (payload instanceof Encrypted) {
|
||||||
object.encrypt(recipient.getPubkey());
|
object.encrypt(recipient.getPubkey());
|
||||||
}
|
}
|
||||||
proofOfWorkService.doProofOfWork(to, object);
|
proofOfWorkService.doProofOfWork(to, object);
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ApplicationException(e);
|
throw new ApplicationException(e);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package ch.dissem.bitmessage;
|
package ch.dissem.bitmessage;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.*;
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.payload.Broadcast;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.payload.Msg;
|
||||||
import ch.dissem.bitmessage.entity.PlaintextHolder;
|
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||||
import ch.dissem.bitmessage.ports.Cryptography;
|
import ch.dissem.bitmessage.ports.Cryptography;
|
||||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
|
||||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import static ch.dissem.bitmessage.InternalContext.NETWORK_EXTRA_BYTES;
|
import static ch.dissem.bitmessage.InternalContext.NETWORK_EXTRA_BYTES;
|
||||||
import static ch.dissem.bitmessage.InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE;
|
import static ch.dissem.bitmessage.InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
@ -35,7 +36,7 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC
|
|||||||
|
|
||||||
LOG.info("Doing POW for " + items.size() + " tasks.");
|
LOG.info("Doing POW for " + items.size() + " tasks.");
|
||||||
for (byte[] initialHash : items) {
|
for (byte[] initialHash : items) {
|
||||||
ProofOfWorkRepository.Item item = powRepo.getItem(initialHash);
|
Item item = powRepo.getItem(initialHash);
|
||||||
cryptography.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, this);
|
cryptography.doProofOfWork(item.object, item.nonceTrialsPerByte, item.extraBytes, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,8 +60,18 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC
|
|||||||
cryptography.doProofOfWork(object, nonceTrialsPerByte, extraBytes, this);
|
cryptography.doProofOfWork(object, nonceTrialsPerByte, extraBytes, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void doProofOfWorkWithAck(Plaintext plaintext, long expirationTime) {
|
||||||
|
final ObjectMessage ack = plaintext.getAckMessage();
|
||||||
|
Item item = new Item(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES,
|
||||||
|
expirationTime, plaintext);
|
||||||
|
powRepo.putObject(item);
|
||||||
|
cryptography.doProofOfWork(ack, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES, this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
|
public void onNonceCalculated(byte[] initialHash, byte[] nonce) {
|
||||||
|
Item item = powRepo.getItem(initialHash);
|
||||||
|
if (item.message == null) {
|
||||||
ObjectMessage object = powRepo.getItem(initialHash).object;
|
ObjectMessage object = powRepo.getItem(initialHash).object;
|
||||||
object.setNonce(nonce);
|
object.setNonce(nonce);
|
||||||
Plaintext plaintext = messageRepo.getMessage(initialHash);
|
Plaintext plaintext = messageRepo.getMessage(initialHash);
|
||||||
@ -72,12 +83,27 @@ public class ProofOfWorkService implements ProofOfWorkEngine.Callback, InternalC
|
|||||||
ctx.getInventory().storeObject(object);
|
ctx.getInventory().storeObject(object);
|
||||||
powRepo.removeObject(initialHash);
|
powRepo.removeObject(initialHash);
|
||||||
ctx.getNetworkHandler().offer(object.getInventoryVector());
|
ctx.getNetworkHandler().offer(object.getInventoryVector());
|
||||||
|
} else {
|
||||||
|
item.message.getAckMessage().setNonce(nonce);
|
||||||
|
final ObjectMessage object = new ObjectMessage.Builder()
|
||||||
|
.stream(item.message.getStream())
|
||||||
|
.expiresTime(item.expirationTime)
|
||||||
|
.payload(new Msg(item.message))
|
||||||
|
.build();
|
||||||
|
if (object.isSigned()) {
|
||||||
|
object.sign(item.message.getFrom().getPrivateKey());
|
||||||
|
}
|
||||||
|
if (object.getPayload() instanceof Encrypted) {
|
||||||
|
object.encrypt(item.message.getTo().getPubkey());
|
||||||
|
}
|
||||||
|
doProofOfWork(item.message.getTo(), object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContext(InternalContext ctx) {
|
public void setContext(InternalContext ctx) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.cryptography = security();
|
this.cryptography = cryptography();
|
||||||
this.powRepo = ctx.getProofOfWorkRepository();
|
this.powRepo = ctx.getProofOfWorkRepository();
|
||||||
this.messageRepo = ctx.getMessageRepository();
|
this.messageRepo = ctx.getMessageRepository();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Decode.bytes;
|
import static ch.dissem.bitmessage.utils.Decode.bytes;
|
||||||
import static ch.dissem.bitmessage.utils.Decode.varInt;
|
import static ch.dissem.bitmessage.utils.Decode.varInt;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Bitmessage address. Can be a user's private address, an address string without public keys or a recipient's address
|
* A Bitmessage address. Can be a user's private address, an address string without public keys or a recipient's address
|
||||||
@ -74,19 +74,19 @@ public class BitmessageAddress implements Serializable {
|
|||||||
Encode.varInt(version, os);
|
Encode.varInt(version, os);
|
||||||
Encode.varInt(stream, os);
|
Encode.varInt(stream, os);
|
||||||
if (version < 4) {
|
if (version < 4) {
|
||||||
byte[] checksum = security().sha512(os.toByteArray(), ripe);
|
byte[] checksum = cryptography().sha512(os.toByteArray(), ripe);
|
||||||
this.tag = null;
|
this.tag = null;
|
||||||
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
||||||
} else {
|
} else {
|
||||||
// for tag and decryption key, the checksum has to be created with 0x00 padding
|
// for tag and decryption key, the checksum has to be created with 0x00 padding
|
||||||
byte[] checksum = security().doubleSha512(os.toByteArray(), ripe);
|
byte[] checksum = cryptography().doubleSha512(os.toByteArray(), ripe);
|
||||||
this.tag = Arrays.copyOfRange(checksum, 32, 64);
|
this.tag = Arrays.copyOfRange(checksum, 32, 64);
|
||||||
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
||||||
}
|
}
|
||||||
// but for the address and its checksum they need to be stripped
|
// but for the address and its checksum they need to be stripped
|
||||||
int offset = Bytes.numberOfLeadingZeros(ripe);
|
int offset = Bytes.numberOfLeadingZeros(ripe);
|
||||||
os.write(ripe, offset, ripe.length - offset);
|
os.write(ripe, offset, ripe.length - offset);
|
||||||
byte[] checksum = security().doubleSha512(os.toByteArray());
|
byte[] checksum = cryptography().doubleSha512(os.toByteArray());
|
||||||
os.write(checksum, 0, 4);
|
os.write(checksum, 0, 4);
|
||||||
this.address = "BM-" + Base58.encode(os.toByteArray());
|
this.address = "BM-" + Base58.encode(os.toByteArray());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -147,18 +147,18 @@ public class BitmessageAddress implements Serializable {
|
|||||||
this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20);
|
this.ripe = Bytes.expand(bytes(in, bytes.length - counter.length() - 4), 20);
|
||||||
|
|
||||||
// test checksum
|
// test checksum
|
||||||
byte[] checksum = security().doubleSha512(bytes, bytes.length - 4);
|
byte[] checksum = cryptography().doubleSha512(bytes, bytes.length - 4);
|
||||||
byte[] expectedChecksum = bytes(in, 4);
|
byte[] expectedChecksum = bytes(in, 4);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (expectedChecksum[i] != checksum[i])
|
if (expectedChecksum[i] != checksum[i])
|
||||||
throw new IllegalArgumentException("Checksum of address failed");
|
throw new IllegalArgumentException("Checksum of address failed");
|
||||||
}
|
}
|
||||||
if (version < 4) {
|
if (version < 4) {
|
||||||
checksum = security().sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe);
|
checksum = cryptography().sha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe);
|
||||||
this.tag = null;
|
this.tag = null;
|
||||||
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
||||||
} else {
|
} else {
|
||||||
checksum = security().doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe);
|
checksum = cryptography().doubleSha512(Arrays.copyOfRange(bytes, 0, counter.length()), ripe);
|
||||||
this.tag = Arrays.copyOfRange(checksum, 32, 64);
|
this.tag = Arrays.copyOfRange(checksum, 32, 64);
|
||||||
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
this.publicDecryptionKey = Arrays.copyOfRange(checksum, 0, 32);
|
||||||
}
|
}
|
||||||
@ -173,7 +173,7 @@ public class BitmessageAddress implements Serializable {
|
|||||||
Encode.varInt(version, out);
|
Encode.varInt(version, out);
|
||||||
Encode.varInt(stream, out);
|
Encode.varInt(stream, out);
|
||||||
out.write(ripe);
|
out.write(ripe);
|
||||||
return Arrays.copyOfRange(security().doubleSha512(out.toByteArray()), 32, 64);
|
return Arrays.copyOfRange(cryptography().doubleSha512(out.toByteArray()), 32, 64);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ApplicationException(e);
|
throw new ApplicationException(e);
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A network message is exchanged between two nodes.
|
* A network message is exchanged between two nodes.
|
||||||
@ -51,7 +51,7 @@ public class NetworkMessage implements Streamable {
|
|||||||
* First 4 bytes of sha512(payload)
|
* First 4 bytes of sha512(payload)
|
||||||
*/
|
*/
|
||||||
private byte[] getChecksum(byte[] bytes) throws NoSuchProviderException, NoSuchAlgorithmException {
|
private byte[] getChecksum(byte[] bytes) throws NoSuchProviderException, NoSuchAlgorithmException {
|
||||||
byte[] d = security().sha512(bytes);
|
byte[] d = cryptography().sha512(bytes);
|
||||||
return new byte[]{d[0], d[1], d[2], d[3]};
|
return new byte[]{d[0], d[1], d[2], d[3]};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ import java.io.OutputStream;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The 'object' command sends an object that is shared throughout the network.
|
* The 'object' command sends an object that is shared throughout the network.
|
||||||
@ -96,7 +96,7 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
|
|
||||||
public InventoryVector getInventoryVector() {
|
public InventoryVector getInventoryVector() {
|
||||||
return new InventoryVector(
|
return new InventoryVector(
|
||||||
Bytes.truncate(security().doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32)
|
Bytes.truncate(cryptography().doubleSha512(nonce, getPayloadBytesWithoutNonce()), 32)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
|
|
||||||
public void sign(PrivateKey key) {
|
public void sign(PrivateKey key) {
|
||||||
if (payload.isSigned()) {
|
if (payload.isSigned()) {
|
||||||
payload.setSignature(security().getSignature(getBytesToSign(), key));
|
payload.setSignature(cryptography().getSignature(getBytesToSign(), key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
|
|
||||||
public boolean isSignatureValid(Pubkey pubkey) throws IOException {
|
public boolean isSignatureValid(Pubkey pubkey) throws IOException {
|
||||||
if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first");
|
if (isEncrypted()) throw new IllegalStateException("Payload must be decrypted first");
|
||||||
return security().isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey);
|
return cryptography().isSignatureValid(getBytesToSign(), payload.getSignature(), pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,7 +28,7 @@ import ch.dissem.bitmessage.utils.UnixTime;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unencrypted message to be sent by 'msg' or 'broadcast'.
|
* The unencrypted message to be sent by 'msg' or 'broadcast'.
|
||||||
@ -511,7 +511,7 @@ public class Plaintext implements Streamable {
|
|||||||
to = new BitmessageAddress(0, 0, destinationRipe);
|
to = new BitmessageAddress(0, 0, destinationRipe);
|
||||||
}
|
}
|
||||||
if (type == Type.MSG && ackMessage == null && ackData == null) {
|
if (type == Type.MSG && ackMessage == null && ackData == null) {
|
||||||
ackData = security().randomBytes(32);
|
ackData = cryptography().randomBytes(32);
|
||||||
}
|
}
|
||||||
return new Plaintext(this);
|
return new Plaintext(this);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ import java.io.IOException;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST;
|
import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users who are subscribed to the sending address will see the message appear in their inbox.
|
* Users who are subscribed to the sending address will see the message appear in their inbox.
|
||||||
@ -81,7 +81,7 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted, Plai
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void encrypt() throws IOException {
|
public void encrypt() throws IOException {
|
||||||
encrypt(security().createPublicKey(plaintext.getFrom().getPublicDecryptionKey()));
|
encrypt(cryptography().createPublicKey(plaintext.getFrom().getPublicDecryptionKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,7 +27,7 @@ import java.io.*;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE;
|
import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
|
|
||||||
public class CryptoBox implements Streamable {
|
public class CryptoBox implements Streamable {
|
||||||
@ -50,22 +50,22 @@ public class CryptoBox implements Streamable {
|
|||||||
|
|
||||||
// 1. The destination public key is called K.
|
// 1. The destination public key is called K.
|
||||||
// 2. Generate 16 random bytes using a secure random number generator. Call them IV.
|
// 2. Generate 16 random bytes using a secure random number generator. Call them IV.
|
||||||
initializationVector = security().randomBytes(16);
|
initializationVector = cryptography().randomBytes(16);
|
||||||
|
|
||||||
// 3. Generate a new random EC key pair with private key called r and public key called R.
|
// 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);
|
byte[] r = cryptography().randomBytes(PRIVATE_KEY_SIZE);
|
||||||
R = security().createPublicKey(r);
|
R = cryptography().createPublicKey(r);
|
||||||
// 4. Do an EC point multiply with public key K and private key r. This gives you public key P.
|
// 4. Do an EC point multiply with public key K and private key r. This gives you public key P.
|
||||||
byte[] P = security().multiply(K, r);
|
byte[] P = cryptography().multiply(K, r);
|
||||||
byte[] X = Points.getX(P);
|
byte[] X = Points.getX(P);
|
||||||
// 5. Use the X component of public key P and calculate the SHA512 hash H.
|
// 5. Use the X component of public key P and calculate the SHA512 hash H.
|
||||||
byte[] H = security().sha512(X);
|
byte[] H = cryptography().sha512(X);
|
||||||
// 6. The first 32 bytes of H are called key_e and the last 32 bytes are called key_m.
|
// 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_e = Arrays.copyOfRange(H, 0, 32);
|
||||||
byte[] key_m = Arrays.copyOfRange(H, 32, 64);
|
byte[] key_m = Arrays.copyOfRange(H, 32, 64);
|
||||||
// 7. Pad the input text to a multiple of 16 bytes, in accordance to PKCS7.
|
// 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.
|
// 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 = security().crypt(true, data, key_e, initializationVector);
|
encrypted = cryptography().crypt(true, 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.
|
// 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);
|
mac = calculateMac(key_m);
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ public class CryptoBox implements Streamable {
|
|||||||
private CryptoBox(Builder builder) {
|
private CryptoBox(Builder builder) {
|
||||||
initializationVector = builder.initializationVector;
|
initializationVector = builder.initializationVector;
|
||||||
curveType = builder.curveType;
|
curveType = builder.curveType;
|
||||||
R = security().createPoint(builder.xComponent, builder.yComponent);
|
R = cryptography().createPoint(builder.xComponent, builder.yComponent);
|
||||||
encrypted = builder.encrypted;
|
encrypted = builder.encrypted;
|
||||||
mac = builder.mac;
|
mac = builder.mac;
|
||||||
}
|
}
|
||||||
@ -101,9 +101,9 @@ public class CryptoBox implements Streamable {
|
|||||||
public InputStream decrypt(byte[] k) throws DecryptionFailedException {
|
public InputStream decrypt(byte[] k) throws DecryptionFailedException {
|
||||||
// 1. The private key used to decrypt is called k.
|
// 1. The private key used to decrypt is called k.
|
||||||
// 2. Do an EC point multiply with private key k and public key R. This gives you public key P.
|
// 2. Do an EC point multiply with private key k and public key R. This gives you public key P.
|
||||||
byte[] P = security().multiply(R, k);
|
byte[] P = cryptography().multiply(R, k);
|
||||||
// 3. Use the X component of public key P and calculate the SHA512 hash H.
|
// 3. Use the X component of public key P and calculate the SHA512 hash H.
|
||||||
byte[] H = security().sha512(Arrays.copyOfRange(P, 1, 33));
|
byte[] H = cryptography().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.
|
// 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_e = Arrays.copyOfRange(H, 0, 32);
|
||||||
byte[] key_m = Arrays.copyOfRange(H, 32, 64);
|
byte[] key_m = Arrays.copyOfRange(H, 32, 64);
|
||||||
@ -116,14 +116,14 @@ public class CryptoBox implements Streamable {
|
|||||||
|
|
||||||
// 7. Decrypt the cipher text with AES-256-CBC, using IV as initialization vector, key_e as decryption key
|
// 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.
|
// and the cipher text as payload. The output is the padded input text.
|
||||||
return new ByteArrayInputStream(security().crypt(false, encrypted, key_e, initializationVector));
|
return new ByteArrayInputStream(cryptography().crypt(false, encrypted, key_e, initializationVector));
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] calculateMac(byte[] key_m) {
|
private byte[] calculateMac(byte[] key_m) {
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream macData = new ByteArrayOutputStream();
|
ByteArrayOutputStream macData = new ByteArrayOutputStream();
|
||||||
writeWithoutMAC(macData);
|
writeWithoutMAC(macData);
|
||||||
return security().mac(key_m, macData.toByteArray());
|
return cryptography().mac(key_m, macData.toByteArray());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ApplicationException(e);
|
throw new ApplicationException(e);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public keys for signing and encryption, the answer to a 'getpubkey' request.
|
* Public keys for signing and encryption, the answer to a 'getpubkey' request.
|
||||||
@ -35,7 +35,7 @@ public abstract class Pubkey extends ObjectPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) {
|
public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) {
|
||||||
return security().ripemd160(security().sha512(publicSigningKey, publicEncryptionKey));
|
return cryptography().ripemd160(cryptography().sha512(publicSigningKey, publicEncryptionKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract byte[] getSigningKey();
|
public abstract byte[] getSigningKey();
|
||||||
@ -45,7 +45,7 @@ public abstract class Pubkey extends ObjectPayload {
|
|||||||
public abstract int getBehaviorBitfield();
|
public abstract int getBehaviorBitfield();
|
||||||
|
|
||||||
public byte[] getRipe() {
|
public byte[] getRipe() {
|
||||||
return security().ripemd160(security().sha512(getSigningKey(), getEncryptionKey()));
|
return cryptography().ripemd160(cryptography().sha512(getSigningKey(), getEncryptionKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNonceTrialsPerByte() {
|
public long getNonceTrialsPerByte() {
|
||||||
|
@ -30,7 +30,7 @@ import java.io.*;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a private key. Additional information (stream, version, features, ...) is stored in the accompanying
|
* Represents a private key. Additional information (stream, version, features, ...) is stored in the accompanying
|
||||||
@ -53,15 +53,15 @@ public class PrivateKey implements Streamable {
|
|||||||
byte[] pubEK;
|
byte[] pubEK;
|
||||||
byte[] ripe;
|
byte[] ripe;
|
||||||
do {
|
do {
|
||||||
privSK = security().randomBytes(PRIVATE_KEY_SIZE);
|
privSK = cryptography().randomBytes(PRIVATE_KEY_SIZE);
|
||||||
privEK = security().randomBytes(PRIVATE_KEY_SIZE);
|
privEK = cryptography().randomBytes(PRIVATE_KEY_SIZE);
|
||||||
pubSK = security().createPublicKey(privSK);
|
pubSK = cryptography().createPublicKey(privSK);
|
||||||
pubEK = security().createPublicKey(privEK);
|
pubEK = cryptography().createPublicKey(privEK);
|
||||||
ripe = Pubkey.getRipe(pubSK, pubEK);
|
ripe = Pubkey.getRipe(pubSK, pubEK);
|
||||||
} while (ripe[0] != 0 || (shorter && ripe[1] != 0));
|
} while (ripe[0] != 0 || (shorter && ripe[1] != 0));
|
||||||
this.privateSigningKey = privSK;
|
this.privateSigningKey = privSK;
|
||||||
this.privateEncryptionKey = privEK;
|
this.privateEncryptionKey = privEK;
|
||||||
this.pubkey = security().createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey,
|
this.pubkey = cryptography().createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey,
|
||||||
nonceTrialsPerByte, extraBytes, features);
|
nonceTrialsPerByte, extraBytes, features);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,11 +118,11 @@ public class PrivateKey implements Streamable {
|
|||||||
long encryptionKeyNonce = nextNonce + 1;
|
long encryptionKeyNonce = nextNonce + 1;
|
||||||
byte[] ripe;
|
byte[] ripe;
|
||||||
do {
|
do {
|
||||||
privEK = Bytes.truncate(security().sha512(seed, Encode.varInt(encryptionKeyNonce)), 32);
|
privEK = Bytes.truncate(cryptography().sha512(seed, Encode.varInt(encryptionKeyNonce)), 32);
|
||||||
privSK = Bytes.truncate(security().sha512(seed, Encode.varInt(signingKeyNonce)), 32);
|
privSK = Bytes.truncate(cryptography().sha512(seed, Encode.varInt(signingKeyNonce)), 32);
|
||||||
pubSK = security().createPublicKey(privSK);
|
pubSK = cryptography().createPublicKey(privSK);
|
||||||
pubEK = security().createPublicKey(privEK);
|
pubEK = cryptography().createPublicKey(privEK);
|
||||||
ripe = security().ripemd160(security().sha512(pubSK, pubEK));
|
ripe = cryptography().ripemd160(cryptography().sha512(pubSK, pubEK));
|
||||||
|
|
||||||
signingKeyNonce += 2;
|
signingKeyNonce += 2;
|
||||||
encryptionKeyNonce += 2;
|
encryptionKeyNonce += 2;
|
||||||
|
@ -32,7 +32,7 @@ import java.net.SocketException;
|
|||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.payload.ObjectType.MSG;
|
import static ch.dissem.bitmessage.entity.payload.ObjectType.MSG;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link NetworkMessage} objects from {@link InputStream InputStreams}
|
* Creates {@link NetworkMessage} objects from {@link InputStream InputStreams}
|
||||||
@ -117,8 +117,8 @@ public class Factory {
|
|||||||
BitmessageAddress temp = new BitmessageAddress(address);
|
BitmessageAddress temp = new BitmessageAddress(address);
|
||||||
PrivateKey privateKey = new PrivateKey(privateSigningKey, privateEncryptionKey,
|
PrivateKey privateKey = new PrivateKey(privateSigningKey, privateEncryptionKey,
|
||||||
createPubkey(temp.getVersion(), temp.getStream(),
|
createPubkey(temp.getVersion(), temp.getStream(),
|
||||||
security().createPublicKey(privateSigningKey),
|
cryptography().createPublicKey(privateSigningKey),
|
||||||
security().createPublicKey(privateEncryptionKey),
|
cryptography().createPublicKey(privateEncryptionKey),
|
||||||
nonceTrialsPerByte, extraBytes, behaviourBitfield));
|
nonceTrialsPerByte, extraBytes, behaviourBitfield));
|
||||||
BitmessageAddress result = new BitmessageAddress(privateKey);
|
BitmessageAddress result = new BitmessageAddress(privateKey);
|
||||||
if (!result.getAddress().equals(address)) {
|
if (!result.getAddress().equals(address)) {
|
||||||
|
@ -32,7 +32,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.NetworkMessage.MAGIC_BYTES;
|
import static ch.dissem.bitmessage.entity.NetworkMessage.MAGIC_BYTES;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates protocol v3 network messages from {@link InputStream InputStreams}
|
* Creates protocol v3 network messages from {@link InputStream InputStreams}
|
||||||
@ -183,7 +183,7 @@ class V3MessageFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean testChecksum(byte[] checksum, byte[] payload) {
|
private static boolean testChecksum(byte[] checksum, byte[] payload) {
|
||||||
byte[] payloadChecksum = security().sha512(payload);
|
byte[] payloadChecksum = cryptography().sha512(payload);
|
||||||
for (int i = 0; i < checksum.length; i++) {
|
for (int i = 0; i < checksum.length; i++) {
|
||||||
if (checksum[i] != payloadChecksum[i]) {
|
if (checksum[i] != payloadChecksum[i]) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -30,6 +30,8 @@ public interface MessageRepository {
|
|||||||
|
|
||||||
int countUnread(Label label);
|
int countUnread(Label label);
|
||||||
|
|
||||||
|
Plaintext getMessage(Object id);
|
||||||
|
|
||||||
Plaintext getMessage(byte[] initialHash);
|
Plaintext getMessage(byte[] initialHash);
|
||||||
|
|
||||||
List<Plaintext> findMessages(Label label);
|
List<Plaintext> findMessages(Label label);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ch.dissem.bitmessage.ports;
|
package ch.dissem.bitmessage.ports;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -16,6 +18,8 @@ public interface ProofOfWorkRepository {
|
|||||||
|
|
||||||
void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes);
|
void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes);
|
||||||
|
|
||||||
|
void putObject(Item item);
|
||||||
|
|
||||||
void removeObject(byte[] initialHash);
|
void removeObject(byte[] initialHash);
|
||||||
|
|
||||||
class Item {
|
class Item {
|
||||||
@ -23,10 +27,20 @@ public interface ProofOfWorkRepository {
|
|||||||
public final long nonceTrialsPerByte;
|
public final long nonceTrialsPerByte;
|
||||||
public final long extraBytes;
|
public final long extraBytes;
|
||||||
|
|
||||||
|
// Needed for ACK POW calculation
|
||||||
|
public final Long expirationTime;
|
||||||
|
public final Plaintext message;
|
||||||
|
|
||||||
public Item(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
public Item(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
||||||
|
this(object, nonceTrialsPerByte, extraBytes, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item(ObjectMessage object, long nonceTrialsPerByte, long extraBytes, long expirationTime, Plaintext message) {
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.nonceTrialsPerByte = nonceTrialsPerByte;
|
this.nonceTrialsPerByte = nonceTrialsPerByte;
|
||||||
this.extraBytes = extraBytes;
|
this.extraBytes = extraBytes;
|
||||||
|
this.expirationTime = expirationTime;
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Cryptography security() {
|
public static Cryptography cryptography() {
|
||||||
return cryptography;
|
return cryptography;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,19 +24,20 @@ import ch.dissem.bitmessage.entity.payload.ObjectType;
|
|||||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||||
import ch.dissem.bitmessage.ports.*;
|
import ch.dissem.bitmessage.ports.*;
|
||||||
import ch.dissem.bitmessage.utils.*;
|
import ch.dissem.bitmessage.utils.MessageMatchers;
|
||||||
|
import ch.dissem.bitmessage.utils.Singleton;
|
||||||
|
import ch.dissem.bitmessage.utils.TTL;
|
||||||
|
import ch.dissem.bitmessage.utils.TestUtils;
|
||||||
import org.hamcrest.BaseMatcher;
|
import org.hamcrest.BaseMatcher;
|
||||||
import org.hamcrest.Description;
|
import org.hamcrest.Description;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.payload.ObjectType.*;
|
import static ch.dissem.bitmessage.entity.payload.ObjectType.*;
|
||||||
import static ch.dissem.bitmessage.utils.MessageMatchers.object;
|
import static ch.dissem.bitmessage.utils.MessageMatchers.object;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
|
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
@ -79,9 +80,14 @@ public class BitmessageContextTest {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putObject(Item item) {
|
||||||
|
items.put(new InventoryVector(cryptography().getInitialHash(item.object)), item);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
||||||
items.put(new InventoryVector(security().getInitialHash(object)), new Item(object, nonceTrialsPerByte, extraBytes));
|
items.put(new InventoryVector(cryptography().getInitialHash(object)), new Item(object, nonceTrialsPerByte, extraBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -196,7 +202,7 @@ public class BitmessageContextTest {
|
|||||||
public void ensureMessageIsSent() throws Exception {
|
public void ensureMessageIsSent() throws Exception {
|
||||||
ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), TestUtils.loadContact(),
|
ctx.send(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), TestUtils.loadContact(),
|
||||||
"Subject", "Message");
|
"Subject", "Message");
|
||||||
assertEquals(1, ctx.internals().getProofOfWorkRepository().getItems().size());
|
assertEquals(2, ctx.internals().getProofOfWorkRepository().getItems().size());
|
||||||
verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce())
|
verify(ctx.internals().getProofOfWorkRepository(), timeout(10000).atLeastOnce())
|
||||||
.putObject(object(MSG), eq(1000L), eq(1000L));
|
.putObject(object(MSG), eq(1000L), eq(1000L));
|
||||||
verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Plaintext.Type.MSG));
|
verify(ctx.messages(), timeout(10000).atLeastOnce()).save(MessageMatchers.plaintext(Plaintext.Type.MSG));
|
||||||
|
@ -99,7 +99,7 @@ public class DefaultMessageListenerTest extends TestBase {
|
|||||||
.payload(identity.getPubkey())
|
.payload(identity.getPubkey())
|
||||||
.build();
|
.build();
|
||||||
objectMessage.sign(identity.getPrivateKey());
|
objectMessage.sign(identity.getPrivateKey());
|
||||||
objectMessage.encrypt(Singleton.security().createPublicKey(identity.getPublicDecryptionKey()));
|
objectMessage.encrypt(Singleton.cryptography().createPublicKey(identity.getPublicDecryptionKey()));
|
||||||
listener.receive(objectMessage);
|
listener.receive(objectMessage);
|
||||||
|
|
||||||
verify(addressRepo).save(any(BitmessageAddress.class));
|
verify(addressRepo).save(any(BitmessageAddress.class));
|
||||||
|
@ -30,14 +30,14 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
public class EncryptionTest extends TestBase {
|
public class EncryptionTest extends TestBase {
|
||||||
@Test
|
@Test
|
||||||
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException {
|
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException {
|
||||||
GenericPayload before = new GenericPayload(0, 1, security().randomBytes(100));
|
GenericPayload before = new GenericPayload(0, 1, cryptography().randomBytes(100));
|
||||||
|
|
||||||
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
|
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
|
||||||
CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey());
|
CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey());
|
||||||
|
@ -28,7 +28,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK;
|
import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.DOES_ACK;
|
||||||
import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.INCLUDE_DESTINATION;
|
import static ch.dissem.bitmessage.entity.payload.Pubkey.Feature.INCLUDE_DESTINATION;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class BitmessageAddressTest extends TestBase {
|
public class BitmessageAddressTest extends TestBase {
|
||||||
@ -126,7 +126,7 @@ public class BitmessageAddressTest extends TestBase {
|
|||||||
System.out.println("\n\n" + Strings.hex(privsigningkey) + "\n\n");
|
System.out.println("\n\n" + Strings.hex(privsigningkey) + "\n\n");
|
||||||
|
|
||||||
BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey,
|
BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey,
|
||||||
security().createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000)));
|
cryptography().createPubkey(3, 1, privsigningkey, privencryptionkey, 320, 14000)));
|
||||||
assertEquals(address_string, address.getAddress());
|
assertEquals(address_string, address.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ public class BitmessageAddressTest extends TestBase {
|
|||||||
byte[] privsigningkey = getSecret("5KMWqfCyJZGFgW6QrnPJ6L9Gatz25B51y7ErgqNr1nXUVbtZbdU");
|
byte[] privsigningkey = getSecret("5KMWqfCyJZGFgW6QrnPJ6L9Gatz25B51y7ErgqNr1nXUVbtZbdU");
|
||||||
byte[] privencryptionkey = getSecret("5JXXWEuhHQEPk414SzEZk1PHDRi8kCuZd895J7EnKeQSahJPxGz");
|
byte[] privencryptionkey = getSecret("5JXXWEuhHQEPk414SzEZk1PHDRi8kCuZd895J7EnKeQSahJPxGz");
|
||||||
BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey,
|
BitmessageAddress address = new BitmessageAddress(new PrivateKey(privsigningkey, privencryptionkey,
|
||||||
security().createPubkey(4, 1, privsigningkey, privencryptionkey, 320, 14000)));
|
cryptography().createPubkey(4, 1, privsigningkey, privencryptionkey, 320, 14000)));
|
||||||
assertEquals("BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke", address.getAddress());
|
assertEquals("BM-2cV5f9EpzaYARxtoruSpa6pDoucSf9ZNke", address.getAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ public class BitmessageAddressTest extends TestBase {
|
|||||||
if (bytes.length != 37)
|
if (bytes.length != 37)
|
||||||
throw new IOException("Unknown format: 37 bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long");
|
throw new IOException("Unknown format: 37 bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long");
|
||||||
|
|
||||||
byte[] hash = security().doubleSha256(bytes, 33);
|
byte[] hash = cryptography().doubleSha256(bytes, 33);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat);
|
if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
|
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class SerializationTest extends TestBase {
|
public class SerializationTest extends TestBase {
|
||||||
@ -102,7 +102,7 @@ public class SerializationTest extends TestBase {
|
|||||||
public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception {
|
public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception {
|
||||||
ArrayList<InventoryVector> ivs = new ArrayList<>(50000);
|
ArrayList<InventoryVector> ivs = new ArrayList<>(50000);
|
||||||
for (int i = 0; i < 50000; i++) {
|
for (int i = 0; i < 50000; i++) {
|
||||||
ivs.add(new InventoryVector(security().randomBytes(32)));
|
ivs.add(new InventoryVector(cryptography().randomBytes(32)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Inv inv = new Inv.Builder().inventory(ivs).build();
|
Inv inv = new Inv.Builder().inventory(ivs).build();
|
||||||
|
@ -21,7 +21,7 @@ import ch.dissem.bitmessage.utils.CallbackWaiter;
|
|||||||
import ch.dissem.bitmessage.utils.TestBase;
|
import ch.dissem.bitmessage.utils.TestBase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public class ProofOfWorkEngineTest extends TestBase {
|
public class ProofOfWorkEngineTest extends TestBase {
|
||||||
@ -36,7 +36,7 @@ public class ProofOfWorkEngineTest extends TestBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void testPOW(ProofOfWorkEngine engine) throws InterruptedException {
|
private void testPOW(ProofOfWorkEngine engine) throws InterruptedException {
|
||||||
byte[] initialHash = security().sha512(new byte[]{1, 3, 6, 4});
|
byte[] initialHash = cryptography().sha512(new byte[]{1, 3, 6, 4});
|
||||||
byte[] target = {0, 0, 0, -1, -1, -1, -1, -1};
|
byte[] target = {0, 0, 0, -1, -1, -1, -1, -1};
|
||||||
|
|
||||||
final CallbackWaiter<byte[]> waiter1 = new CallbackWaiter<>();
|
final CallbackWaiter<byte[]> waiter1 = new CallbackWaiter<>();
|
||||||
@ -49,10 +49,10 @@ public class ProofOfWorkEngineTest extends TestBase {
|
|||||||
});
|
});
|
||||||
byte[] nonce = waiter1.waitForValue();
|
byte[] nonce = waiter1.waitForValue();
|
||||||
System.out.println("Calculating nonce took " + waiter1.getTime() + "ms");
|
System.out.println("Calculating nonce took " + waiter1.getTime() + "ms");
|
||||||
assertTrue(Bytes.lt(security().doubleSha512(nonce, initialHash), target, 8));
|
assertTrue(Bytes.lt(cryptography().doubleSha512(nonce, initialHash), target, 8));
|
||||||
|
|
||||||
// Let's add a second (shorter) run to find possible multi threading issues
|
// Let's add a second (shorter) run to find possible multi threading issues
|
||||||
byte[] initialHash2 = security().sha512(new byte[]{1, 3, 6, 5});
|
byte[] initialHash2 = cryptography().sha512(new byte[]{1, 3, 6, 5});
|
||||||
byte[] target2 = {0, 0, -1, -1, -1, -1, -1, -1};
|
byte[] target2 = {0, 0, -1, -1, -1, -1, -1, -1};
|
||||||
|
|
||||||
final CallbackWaiter<byte[]> waiter2 = new CallbackWaiter<>();
|
final CallbackWaiter<byte[]> waiter2 = new CallbackWaiter<>();
|
||||||
@ -65,7 +65,7 @@ public class ProofOfWorkEngineTest extends TestBase {
|
|||||||
});
|
});
|
||||||
byte[] nonce2 = waiter2.waitForValue();
|
byte[] nonce2 = waiter2.waitForValue();
|
||||||
System.out.println("Calculating nonce took " + waiter2.getTime() + "ms");
|
System.out.println("Calculating nonce took " + waiter2.getTime() + "ms");
|
||||||
assertTrue(Bytes.lt(security().doubleSha512(nonce2, initialHash2), target2, 8));
|
assertTrue(Bytes.lt(cryptography().doubleSha512(nonce2, initialHash2), target2, 8));
|
||||||
assertTrue("Second nonce must be quicker to find", waiter1.getTime() > waiter2.getTime());
|
assertTrue("Second nonce must be quicker to find", waiter1.getTime() > waiter2.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import ch.dissem.bitmessage.utils.Encode;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Decode.*;
|
import static ch.dissem.bitmessage.utils.Decode.*;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link CustomMessage} implementation that contains signed and encrypted data.
|
* A {@link CustomMessage} implementation that contains signed and encrypted data.
|
||||||
@ -80,7 +80,7 @@ public class CryptoCustomMessage<T extends Streamable> extends CustomMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data.write(out);
|
data.write(out);
|
||||||
Encode.varBytes(security().getSignature(out.toByteArray(), identity.getPrivateKey()), out);
|
Encode.varBytes(cryptography().getSignature(out.toByteArray(), identity.getPrivateKey()), out);
|
||||||
container = new CryptoBox(out.toByteArray(), publicKey);
|
container = new CryptoBox(out.toByteArray(), publicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,7 +138,7 @@ public class CryptoCustomMessage<T extends Streamable> extends CustomMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void checkSignature(Pubkey pubkey) throws IOException, IllegalStateException {
|
public void checkSignature(Pubkey pubkey) throws IOException, IllegalStateException {
|
||||||
if (!security().isSignatureValid(out.toByteArray(), varBytes(wrapped), pubkey)) {
|
if (!cryptography().isSignatureValid(out.toByteArray(), varBytes(wrapped), pubkey)) {
|
||||||
throw new IllegalStateException("Signature check failed");
|
throw new IllegalStateException("Signature check failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class CryptoCustomMessageTest extends TestBase {
|
public class CryptoCustomMessageTest extends TestBase {
|
||||||
@ -39,9 +39,9 @@ public class CryptoCustomMessageTest extends TestBase {
|
|||||||
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey"));
|
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey"));
|
||||||
BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey);
|
BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey);
|
||||||
|
|
||||||
GenericPayload payloadBefore = new GenericPayload(0, 1, security().randomBytes(100));
|
GenericPayload payloadBefore = new GenericPayload(0, 1, cryptography().randomBytes(100));
|
||||||
CryptoCustomMessage<GenericPayload> messageBefore = new CryptoCustomMessage<>(payloadBefore);
|
CryptoCustomMessage<GenericPayload> messageBefore = new CryptoCustomMessage<>(payloadBefore);
|
||||||
messageBefore.signAndEncrypt(sendingIdentity, security().createPublicKey(sendingIdentity.getPublicDecryptionKey()));
|
messageBefore.signAndEncrypt(sendingIdentity, cryptography().createPublicKey(sendingIdentity.getPublicDecryptionKey()));
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
messageBefore.write(out);
|
messageBefore.write(out);
|
||||||
@ -65,11 +65,11 @@ public class CryptoCustomMessageTest extends TestBase {
|
|||||||
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey"));
|
PrivateKey privateKey = PrivateKey.read(TestUtils.getResource("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8.privkey"));
|
||||||
final BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey);
|
final BitmessageAddress sendingIdentity = new BitmessageAddress(privateKey);
|
||||||
|
|
||||||
ProofOfWorkRequest requestBefore = new ProofOfWorkRequest(sendingIdentity, security().randomBytes(64),
|
ProofOfWorkRequest requestBefore = new ProofOfWorkRequest(sendingIdentity, cryptography().randomBytes(64),
|
||||||
ProofOfWorkRequest.Request.CALCULATE);
|
ProofOfWorkRequest.Request.CALCULATE);
|
||||||
|
|
||||||
CryptoCustomMessage<ProofOfWorkRequest> messageBefore = new CryptoCustomMessage<>(requestBefore);
|
CryptoCustomMessage<ProofOfWorkRequest> messageBefore = new CryptoCustomMessage<>(requestBefore);
|
||||||
messageBefore.signAndEncrypt(sendingIdentity, security().createPublicKey(sendingIdentity.getPublicDecryptionKey()));
|
messageBefore.signAndEncrypt(sendingIdentity, cryptography().createPublicKey(sendingIdentity.getPublicDecryptionKey()));
|
||||||
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
@ -46,7 +46,7 @@ import static ch.dissem.bitmessage.InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE
|
|||||||
import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT;
|
import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT;
|
||||||
import static ch.dissem.bitmessage.networking.Connection.Mode.SYNC;
|
import static ch.dissem.bitmessage.networking.Connection.Mode.SYNC;
|
||||||
import static ch.dissem.bitmessage.networking.Connection.State.*;
|
import static ch.dissem.bitmessage.networking.Connection.State.*;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
|
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,7 +249,7 @@ class Connection {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
listener.receive(objectMessage);
|
listener.receive(objectMessage);
|
||||||
security().checkProofOfWork(objectMessage, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES);
|
cryptography().checkProofOfWork(objectMessage, NETWORK_NONCE_TRIALS_PER_BYTE, NETWORK_EXTRA_BYTES);
|
||||||
ctx.getInventory().storeObject(objectMessage);
|
ctx.getInventory().storeObject(objectMessage);
|
||||||
// offer object to some random nodes so it gets distributed throughout the network:
|
// offer object to some random nodes so it gets distributed throughout the network:
|
||||||
networkHandler.offer(objectMessage.getInventoryVector());
|
networkHandler.offer(objectMessage.getInventoryVector());
|
||||||
|
@ -18,5 +18,6 @@ dependencies {
|
|||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile 'com.h2database:h2:1.4.190'
|
testCompile 'com.h2database:h2:1.4.190'
|
||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||||
|
testCompile project(path: ':core', configuration: 'testArtifacts')
|
||||||
testCompile project(':cryptography-bc')
|
testCompile project(':cryptography-bc')
|
||||||
}
|
}
|
@ -117,6 +117,24 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Plaintext getMessage(Object id) {
|
||||||
|
if (id instanceof Long) {
|
||||||
|
List<Plaintext> plaintexts = find("id=" + id);
|
||||||
|
switch (plaintexts.size()) {
|
||||||
|
case 0:
|
||||||
|
return null;
|
||||||
|
case 1:
|
||||||
|
return plaintexts.get(0);
|
||||||
|
default:
|
||||||
|
throw new ApplicationException("This shouldn't happen, found " + plaintexts.size() +
|
||||||
|
" messages, one or none was expected");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Long expected for ID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Plaintext getMessage(byte[] initialHash) {
|
public Plaintext getMessage(byte[] initialHash) {
|
||||||
List<Plaintext> plaintexts = find("initial_hash=X'" + Strings.hex(initialHash) + "'");
|
List<Plaintext> plaintexts = find("initial_hash=X'" + Strings.hex(initialHash) + "'");
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package ch.dissem.bitmessage.repository;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.InternalContext;
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||||
import ch.dissem.bitmessage.factory.Factory;
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||||
@ -8,18 +10,21 @@ import ch.dissem.bitmessage.utils.Strings;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
*/
|
*/
|
||||||
public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWorkRepository {
|
public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWorkRepository, InternalContext.ContextHolder {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JdbcProofOfWorkRepository.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JdbcProofOfWorkRepository.class);
|
||||||
|
private InternalContext ctx;
|
||||||
|
|
||||||
public JdbcProofOfWorkRepository(JdbcConfig config) {
|
public JdbcProofOfWorkRepository(JdbcConfig config) {
|
||||||
super(config);
|
super(config);
|
||||||
@ -30,17 +35,27 @@ public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWork
|
|||||||
try (
|
try (
|
||||||
Connection connection = config.getConnection();
|
Connection connection = config.getConnection();
|
||||||
PreparedStatement ps = connection.prepareStatement("SELECT data, version, nonce_trials_per_byte, " +
|
PreparedStatement ps = connection.prepareStatement("SELECT data, version, nonce_trials_per_byte, " +
|
||||||
"extra_bytes FROM POW WHERE initial_hash=?")
|
"extra_bytes, expiration_time, message_id FROM POW WHERE initial_hash=?")
|
||||||
) {
|
) {
|
||||||
ps.setBytes(1, initialHash);
|
ps.setBytes(1, initialHash);
|
||||||
try (ResultSet rs = ps.executeQuery()) {
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
if (rs.next()) {
|
if (rs.next()) {
|
||||||
Blob data = rs.getBlob("data");
|
Blob data = rs.getBlob("data");
|
||||||
|
if (rs.getObject("message_id") == null) {
|
||||||
return new Item(
|
return new Item(
|
||||||
Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()),
|
Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()),
|
||||||
rs.getLong("nonce_trials_per_byte"),
|
rs.getLong("nonce_trials_per_byte"),
|
||||||
rs.getLong("extra_bytes")
|
rs.getLong("extra_bytes")
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return new Item(
|
||||||
|
Factory.getObjectMessage(rs.getInt("version"), data.getBinaryStream(), (int) data.length()),
|
||||||
|
rs.getLong("nonce_trials_per_byte"),
|
||||||
|
rs.getLong("extra_bytes"),
|
||||||
|
rs.getLong("expiration_time"),
|
||||||
|
ctx.getMessageRepository().getMessage(rs.getLong("message_id"))
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Object requested that we don't have. Initial hash: " + Strings.hex(initialHash));
|
throw new IllegalArgumentException("Object requested that we don't have. Initial hash: " + Strings.hex(initialHash));
|
||||||
}
|
}
|
||||||
@ -70,24 +85,38 @@ public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWork
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
public void putObject(Item item) {
|
||||||
try (
|
try (
|
||||||
Connection connection = config.getConnection();
|
Connection connection = config.getConnection();
|
||||||
PreparedStatement ps = connection.prepareStatement("INSERT INTO POW (initial_hash, data, version, " +
|
PreparedStatement ps = connection.prepareStatement("INSERT INTO POW (initial_hash, data, version, " +
|
||||||
"nonce_trials_per_byte, extra_bytes) VALUES (?, ?, ?, ?, ?)")
|
"nonce_trials_per_byte, extra_bytes, expiration_time, message_id) " +
|
||||||
|
"VALUES (?, ?, ?, ?, ?, ?, ?)")
|
||||||
) {
|
) {
|
||||||
ps.setBytes(1, security().getInitialHash(object));
|
ps.setBytes(1, cryptography().getInitialHash(item.object));
|
||||||
writeBlob(ps, 2, object);
|
writeBlob(ps, 2, item.object);
|
||||||
ps.setLong(3, object.getVersion());
|
ps.setLong(3, item.object.getVersion());
|
||||||
ps.setLong(4, nonceTrialsPerByte);
|
ps.setLong(4, item.nonceTrialsPerByte);
|
||||||
ps.setLong(5, extraBytes);
|
ps.setLong(5, item.extraBytes);
|
||||||
|
|
||||||
|
if (item.message == null) {
|
||||||
|
ps.setObject(6, null);
|
||||||
|
ps.setObject(7, null);
|
||||||
|
} else {
|
||||||
|
ps.setLong(6, item.expirationTime);
|
||||||
|
ps.setLong(7, (Long) item.message.getId());
|
||||||
|
}
|
||||||
ps.executeUpdate();
|
ps.executeUpdate();
|
||||||
} catch (IOException | SQLException e) {
|
} catch (IOException | SQLException e) {
|
||||||
LOG.debug("Error storing object of type " + object.getPayload().getClass().getSimpleName(), e);
|
LOG.debug("Error storing object of type " + item.object.getPayload().getClass().getSimpleName(), e);
|
||||||
throw new ApplicationException(e);
|
throw new ApplicationException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putObject(ObjectMessage object, long nonceTrialsPerByte, long extraBytes) {
|
||||||
|
putObject(new Item(object, nonceTrialsPerByte, extraBytes));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeObject(byte[] initialHash) {
|
public void removeObject(byte[] initialHash) {
|
||||||
try (
|
try (
|
||||||
@ -100,4 +129,9 @@ public class JdbcProofOfWorkRepository extends JdbcHelper implements ProofOfWork
|
|||||||
LOG.debug(e.getMessage(), e);
|
LOG.debug(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContext(InternalContext context) {
|
||||||
|
this.ctx = context;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE POW ADD COLUMN expiration_time BIGINT;
|
||||||
|
ALTER TABLE POW ADD COLUMN message_id BIGINT;
|
@ -68,9 +68,11 @@ public class JdbcAddressRepositoryTest extends TestBase {
|
|||||||
public void testFindIdentity() throws Exception {
|
public void testFindIdentity() throws Exception {
|
||||||
BitmessageAddress identity = new BitmessageAddress(IDENTITY_A);
|
BitmessageAddress identity = new BitmessageAddress(IDENTITY_A);
|
||||||
assertEquals(4, identity.getVersion());
|
assertEquals(4, identity.getVersion());
|
||||||
assertEquals(identity, repo.findIdentity(identity.getTag()));
|
|
||||||
assertNull(repo.findContact(identity.getTag()));
|
assertNull(repo.findContact(identity.getTag()));
|
||||||
assertTrue(identity.has(Pubkey.Feature.DOES_ACK));
|
|
||||||
|
BitmessageAddress storedIdentity = repo.findIdentity(identity.getTag());
|
||||||
|
assertEquals(identity, storedIdentity);
|
||||||
|
assertTrue(storedIdentity.has(Pubkey.Feature.DOES_ACK));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -32,7 +32,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
|
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
|
|||||||
AddressRepository addressRepo = new JdbcAddressRepository(config);
|
AddressRepository addressRepo = new JdbcAddressRepository(config);
|
||||||
repo = new JdbcMessageRepository(config);
|
repo = new JdbcMessageRepository(config);
|
||||||
new InternalContext(new BitmessageContext.Builder()
|
new InternalContext(new BitmessageContext.Builder()
|
||||||
.cryptography(security())
|
.cryptography(cryptography())
|
||||||
.addressRepo(addressRepo)
|
.addressRepo(addressRepo)
|
||||||
.messageRepo(repo)
|
.messageRepo(repo)
|
||||||
);
|
);
|
||||||
@ -146,7 +146,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
|
|||||||
@Test
|
@Test
|
||||||
public void testSave() throws Exception {
|
public void testSave() throws Exception {
|
||||||
Plaintext message = new Plaintext.Builder(MSG)
|
Plaintext message = new Plaintext.Builder(MSG)
|
||||||
.IV(new InventoryVector(security().randomBytes(32)))
|
.IV(new InventoryVector(cryptography().randomBytes(32)))
|
||||||
.from(identity)
|
.from(identity)
|
||||||
.to(contactA)
|
.to(contactA)
|
||||||
.message("Subject", "Message")
|
.message("Subject", "Message")
|
||||||
@ -169,7 +169,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
|
|||||||
public void testUpdate() throws Exception {
|
public void testUpdate() throws Exception {
|
||||||
List<Plaintext> messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
|
List<Plaintext> messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
|
||||||
Plaintext message = messages.get(0);
|
Plaintext message = messages.get(0);
|
||||||
message.setInventoryVector(new InventoryVector(security().randomBytes(32)));
|
message.setInventoryVector(new InventoryVector(cryptography().randomBytes(32)));
|
||||||
repo.save(message);
|
repo.save(message);
|
||||||
|
|
||||||
messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
|
messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
|
||||||
|
@ -16,13 +16,24 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.repository;
|
package ch.dissem.bitmessage.repository;
|
||||||
|
|
||||||
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
import ch.dissem.bitmessage.InternalContext;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.GenericPayload;
|
||||||
import ch.dissem.bitmessage.entity.payload.GetPubkey;
|
import ch.dissem.bitmessage.entity.payload.GetPubkey;
|
||||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||||
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository.Item;
|
||||||
|
import ch.dissem.bitmessage.utils.TestUtils;
|
||||||
|
import ch.dissem.bitmessage.utils.UnixTime;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
|
||||||
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@ -33,17 +44,51 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class JdbcProofOfWorkRepositoryTest extends TestBase {
|
public class JdbcProofOfWorkRepositoryTest extends TestBase {
|
||||||
private TestJdbcConfig config;
|
private TestJdbcConfig config;
|
||||||
private JdbcProofOfWorkRepository repo;
|
private JdbcProofOfWorkRepository repo;
|
||||||
|
private AddressRepository addressRepo;
|
||||||
|
private MessageRepository messageRepo;
|
||||||
|
|
||||||
|
private byte[] initialHash1;
|
||||||
|
private byte[] initialHash2;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
config = new TestJdbcConfig();
|
config = new TestJdbcConfig();
|
||||||
config.reset();
|
config.reset();
|
||||||
|
|
||||||
|
addressRepo = new JdbcAddressRepository(config);
|
||||||
|
messageRepo = new JdbcMessageRepository(config);
|
||||||
repo = new JdbcProofOfWorkRepository(config);
|
repo = new JdbcProofOfWorkRepository(config);
|
||||||
|
InternalContext ctx = new InternalContext(new BitmessageContext.Builder()
|
||||||
|
.addressRepo(addressRepo)
|
||||||
|
.messageRepo(messageRepo)
|
||||||
|
.powRepo(repo)
|
||||||
|
.cryptography(cryptography())
|
||||||
|
);
|
||||||
|
|
||||||
repo.putObject(new ObjectMessage.Builder()
|
repo.putObject(new ObjectMessage.Builder()
|
||||||
.payload(new GetPubkey(new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn"))).build(),
|
.payload(new GetPubkey(new BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn"))).build(),
|
||||||
1000, 1000);
|
1000, 1000);
|
||||||
|
initialHash1 = repo.getItems().get(0);
|
||||||
|
|
||||||
|
BitmessageAddress sender = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8");
|
||||||
|
BitmessageAddress recipient = TestUtils.loadContact();
|
||||||
|
addressRepo.save(sender);
|
||||||
|
addressRepo.save(recipient);
|
||||||
|
Plaintext plaintext = new Plaintext.Builder(MSG)
|
||||||
|
.ackData(cryptography().randomBytes(32))
|
||||||
|
.from(sender)
|
||||||
|
.to(recipient)
|
||||||
|
.message("Subject", "Message")
|
||||||
|
.status(Plaintext.Status.DOING_PROOF_OF_WORK)
|
||||||
|
.build();
|
||||||
|
messageRepo.save(plaintext);
|
||||||
|
initialHash2 = cryptography().getInitialHash(plaintext.getAckMessage());
|
||||||
|
repo.putObject(new Item(
|
||||||
|
plaintext.getAckMessage(),
|
||||||
|
1000, 1000,
|
||||||
|
UnixTime.now(+10 * MINUTE),
|
||||||
|
plaintext
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -55,16 +100,52 @@ public class JdbcProofOfWorkRepositoryTest extends TestBase {
|
|||||||
assertThat(repo.getItems().size(), is(sizeBefore + 1));
|
assertThat(repo.getItems().size(), is(sizeBefore + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureAckObjectsAreStored() throws Exception {
|
||||||
|
int sizeBefore = repo.getItems().size();
|
||||||
|
BitmessageAddress sender = TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8");
|
||||||
|
BitmessageAddress recipient = TestUtils.loadContact();
|
||||||
|
addressRepo.save(sender);
|
||||||
|
addressRepo.save(recipient);
|
||||||
|
Plaintext plaintext = new Plaintext.Builder(MSG)
|
||||||
|
.ackData(cryptography().randomBytes(32))
|
||||||
|
.from(sender)
|
||||||
|
.to(recipient)
|
||||||
|
.message("Subject", "Message")
|
||||||
|
.status(Plaintext.Status.DOING_PROOF_OF_WORK)
|
||||||
|
.build();
|
||||||
|
messageRepo.save(plaintext);
|
||||||
|
repo.putObject(new Item(
|
||||||
|
plaintext.getAckMessage(),
|
||||||
|
1000, 1000,
|
||||||
|
UnixTime.now(+10 * MINUTE),
|
||||||
|
plaintext
|
||||||
|
));
|
||||||
|
assertThat(repo.getItems().size(), is(sizeBefore + 1));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ensureItemCanBeRetrieved() {
|
public void ensureItemCanBeRetrieved() {
|
||||||
byte[] initialHash = repo.getItems().get(0);
|
Item item = repo.getItem(initialHash1);
|
||||||
ProofOfWorkRepository.Item item = repo.getItem(initialHash);
|
|
||||||
assertThat(item, notNullValue());
|
assertThat(item, notNullValue());
|
||||||
assertThat(item.object.getPayload(), instanceOf(GetPubkey.class));
|
assertThat(item.object.getPayload(), instanceOf(GetPubkey.class));
|
||||||
assertThat(item.nonceTrialsPerByte, is(1000L));
|
assertThat(item.nonceTrialsPerByte, is(1000L));
|
||||||
assertThat(item.extraBytes, is(1000L));
|
assertThat(item.extraBytes, is(1000L));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ensureAckItemCanBeRetrieved() {
|
||||||
|
Item item = repo.getItem(initialHash2);
|
||||||
|
assertThat(item, notNullValue());
|
||||||
|
assertThat(item.object.getPayload(), instanceOf(GenericPayload.class));
|
||||||
|
assertThat(item.nonceTrialsPerByte, is(1000L));
|
||||||
|
assertThat(item.extraBytes, is(1000L));
|
||||||
|
assertThat(item.expirationTime, not(0));
|
||||||
|
assertThat(item.message, notNullValue());
|
||||||
|
assertThat(item.message.getFrom().getPrivateKey(), notNullValue());
|
||||||
|
assertThat(item.message.getTo().getPubkey(), notNullValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException.class)
|
@Test(expected = RuntimeException.class)
|
||||||
public void ensureRetrievingNonexistingItemThrowsException() {
|
public void ensureRetrievingNonexistingItemThrowsException() {
|
||||||
repo.getItem(new byte[0]);
|
repo.getItem(new byte[0]);
|
||||||
@ -72,8 +153,8 @@ public class JdbcProofOfWorkRepositoryTest extends TestBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void ensureItemCanBeDeleted() {
|
public void ensureItemCanBeDeleted() {
|
||||||
byte[] initialHash = repo.getItems().get(0);
|
repo.removeObject(initialHash1);
|
||||||
repo.removeObject(initialHash);
|
repo.removeObject(initialHash2);
|
||||||
assertTrue(repo.getItems().isEmpty());
|
assertTrue(repo.getItems().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import java.io.*;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE;
|
import static ch.dissem.bitmessage.entity.valueobject.PrivateKey.PRIVATE_KEY_SIZE;
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
@ -77,7 +77,7 @@ public class WifExporter {
|
|||||||
byte[] result = new byte[37];
|
byte[] result = new byte[37];
|
||||||
result[0] = (byte) 0x80;
|
result[0] = (byte) 0x80;
|
||||||
System.arraycopy(privateKey, 0, result, 1, PRIVATE_KEY_SIZE);
|
System.arraycopy(privateKey, 0, result, 1, PRIVATE_KEY_SIZE);
|
||||||
byte[] hash = security().doubleSha256(result, PRIVATE_KEY_SIZE + 1);
|
byte[] hash = cryptography().doubleSha256(result, PRIVATE_KEY_SIZE + 1);
|
||||||
System.arraycopy(hash, 0, result, PRIVATE_KEY_SIZE + 1, 4);
|
System.arraycopy(hash, 0, result, PRIVATE_KEY_SIZE + 1, 4);
|
||||||
return Base58.encode(result);
|
return Base58.encode(result);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.Singleton.security;
|
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Basler
|
* @author Christian Basler
|
||||||
@ -87,7 +87,7 @@ public class WifImporter {
|
|||||||
throw new IOException("Unknown format: " + WIF_SECRET_LENGTH +
|
throw new IOException("Unknown format: " + WIF_SECRET_LENGTH +
|
||||||
" bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long");
|
" bytes expected, but secret " + walletImportFormat + " was " + bytes.length + " long");
|
||||||
|
|
||||||
byte[] hash = security().doubleSha256(bytes, 33);
|
byte[] hash = cryptography().doubleSha256(bytes, 33);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat);
|
if (hash[i] != bytes[33 + i]) throw new IOException("Hash check failed for secret " + walletImportFormat);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user