Refactored to use StreamableWriter

Bumped the msgpack library to 2.0.1 (the 2.0.0 build was fubar)
This commit is contained in:
Christian Basler 2017-11-21 10:44:41 +01:00
parent ece9cd8667
commit 8cbdce6eac
51 changed files with 1004 additions and 625 deletions

View File

@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.1.4-3'
ext.kotlin_version = '1.1.60'
repositories {
mavenCentral()
}
@ -13,12 +13,12 @@ plugins {
}
subprojects {
apply plugin: 'io.spring.dependency-management'
apply plugin: 'kotlin'
apply plugin: 'maven'
apply plugin: 'signing'
apply plugin: 'jacoco'
apply plugin: 'gitflow-version'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.github.ben-manes.versions'
sourceCompatibility = 1.7
@ -138,7 +138,7 @@ subprojects {
entry 'slf4j-simple'
}
dependency 'ch.dissem.msgpack:msgpack:2.0.0'
dependency 'ch.dissem.msgpack:msgpack:2.0.1'
dependency 'org.bouncycastle:bcprov-jdk15on:1.57'
dependency 'com.madgag.spongycastle:prov:1.56.0.0'
dependency 'org.apache.commons:commons-lang3:3.6'

View File

@ -25,10 +25,10 @@ artifacts {
dependencies {
compile 'org.slf4j:slf4j-api'
compile 'ch.dissem.msgpack:msgpack:1.0.0'
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'com.nhaarman:mockito-kotlin:1.5.0'
compile 'ch.dissem.msgpack:msgpack'
testCompile 'junit:junit'
testCompile 'org.hamcrest:hamcrest-library'
testCompile 'com.nhaarman:mockito-kotlin'
testCompile project(':cryptography-bc')
}

View File

@ -272,6 +272,7 @@ class BitmessageContext(
*/
fun cleanup() {
internals.inventory.cleanup()
internals.nodeRegistry.cleanup()
}
/**

View File

@ -25,19 +25,28 @@ import java.nio.ByteBuffer
* The 'addr' command holds a list of known active Bitmessage nodes.
*/
data class Addr constructor(val addresses: List<NetworkAddress>) : MessagePayload {
override val command: MessagePayload.Command = MessagePayload.Command.ADDR
override fun write(out: OutputStream) {
Encode.varInt(addresses.size, out)
for (address in addresses) {
address.write(out)
}
}
override fun writer(): StreamableWriter = Writer(this)
override fun write(buffer: ByteBuffer) {
Encode.varInt(addresses.size, buffer)
for (address in addresses) {
address.write(buffer)
private class Writer(
private val item: Addr
) : StreamableWriter {
override fun write(out: OutputStream) {
Encode.varInt(item.addresses.size, out)
for (address in item.addresses) {
address.writer().write(out)
}
}
override fun write(buffer: ByteBuffer) {
Encode.varInt(item.addresses.size, buffer)
for (address in item.addresses) {
address.writer().write(buffer)
}
}
}
}

View File

@ -33,36 +33,42 @@ open class CustomMessage(val customCommand: String, private val data: ByteArray?
override val command: MessagePayload.Command = MessagePayload.Command.CUSTOM
val isError: Boolean
val isError = COMMAND_ERROR == customCommand
fun getData(): ByteArray {
if (data != null) {
return data
} else {
return data ?: {
val out = ByteArrayOutputStream()
write(out)
return out.toByteArray()
}
writer().write(out)
out.toByteArray()
}.invoke()
}
override fun write(out: OutputStream) {
if (data != null) {
Encode.varString(customCommand, out)
out.write(data)
} else {
throw ApplicationException("Tried to write custom message without data. "
+ "Programmer: did you forget to override #write()?")
}
}
override fun writer(): StreamableWriter = Writer(this)
override fun write(buffer: ByteBuffer) {
if (data != null) {
Encode.varString(customCommand, buffer)
buffer.put(data)
} else {
throw ApplicationException("Tried to write custom message without data. "
+ "Programmer: did you forget to override #write()?")
protected open class Writer(
private val item: CustomMessage
) : StreamableWriter {
override fun write(out: OutputStream) {
if (item.data != null) {
Encode.varString(item.customCommand, out)
out.write(item.data)
} else {
throw ApplicationException("Tried to write custom message without data. "
+ "Programmer: did you forget to override #write()?")
}
}
override fun write(buffer: ByteBuffer) {
if (item.data != null) {
Encode.varString(item.customCommand, buffer)
buffer.put(item.data)
} else {
throw ApplicationException("Tried to write custom message without data. "
+ "Programmer: did you forget to override #write()?")
}
}
}
companion object {
@ -75,12 +81,6 @@ open class CustomMessage(val customCommand: String, private val data: ByteArray?
}
@JvmStatic
fun error(message: String): CustomMessage {
return CustomMessage(COMMAND_ERROR, message.toByteArray(charset("UTF-8")))
}
}
init {
this.isError = COMMAND_ERROR == customCommand
fun error(message: String) = CustomMessage(COMMAND_ERROR, message.toByteArray(charset("UTF-8")))
}
}

View File

@ -28,21 +28,30 @@ class GetData constructor(var inventory: List<InventoryVector>) : MessagePayload
override val command: MessagePayload.Command = MessagePayload.Command.GETDATA
override fun write(out: OutputStream) {
Encode.varInt(inventory.size, out)
for (iv in inventory) {
iv.write(out)
}
}
override fun writer(): StreamableWriter = Writer(this)
override fun write(buffer: ByteBuffer) {
Encode.varInt(inventory.size, buffer)
for (iv in inventory) {
iv.write(buffer)
private class Writer(
private val item: GetData
) : StreamableWriter {
override fun write(out: OutputStream) {
Encode.varInt(item.inventory.size, out)
for (iv in item.inventory) {
iv.writer().write(out)
}
}
override fun write(buffer: ByteBuffer) {
Encode.varInt(item.inventory.size, buffer)
for (iv in item.inventory) {
iv.writer().write(buffer)
}
}
}
companion object {
@JvmField val MAX_INVENTORY_SIZE = 50000
@JvmField
val MAX_INVENTORY_SIZE = 50000
}
}

View File

@ -28,17 +28,25 @@ class Inv constructor(val inventory: List<InventoryVector>) : MessagePayload {
override val command: MessagePayload.Command = MessagePayload.Command.INV
override fun write(out: OutputStream) {
Encode.varInt(inventory.size, out)
for (iv in inventory) {
iv.write(out)
}
}
override fun writer(): StreamableWriter = Writer(this)
override fun write(buffer: ByteBuffer) {
Encode.varInt(inventory.size, buffer)
for (iv in inventory) {
iv.write(buffer)
private class Writer(
private val item: Inv
) : StreamableWriter {
override fun write(out: OutputStream) {
Encode.varInt(item.inventory.size, out)
for (iv in item.inventory) {
iv.writer().write(out)
}
}
override fun write(buffer: ByteBuffer) {
Encode.varInt(item.inventory.size, buffer)
for (iv in item.inventory) {
iv.writer().write(buffer)
}
}
}
}

View File

@ -18,7 +18,6 @@ package ch.dissem.bitmessage.entity
import ch.dissem.bitmessage.utils.Encode
import ch.dissem.bitmessage.utils.Singleton.cryptography
import java.io.IOException
import java.io.OutputStream
import java.nio.ByteBuffer
@ -32,87 +31,95 @@ data class NetworkMessage(
val payload: MessagePayload
) : Streamable {
/**
* First 4 bytes of sha512(payload)
*/
private fun getChecksum(bytes: ByteArray): ByteArray {
val d = cryptography().sha512(bytes)
return byteArrayOf(d[0], d[1], d[2], d[3])
}
override fun writer(): Writer = Writer(this)
override fun write(out: OutputStream) {
// magic
Encode.int32(MAGIC, out)
class Writer internal constructor(
private val item: NetworkMessage
) : StreamableWriter {
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
val command = payload.command.name.toLowerCase()
out.write(command.toByteArray(charset("ASCII")))
for (i in command.length..11) {
out.write(0x0)
override fun write(out: OutputStream) {
// magic
Encode.int32(MAGIC, out)
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
val command = item.payload.command.name.toLowerCase()
out.write(command.toByteArray(charset("ASCII")))
for (i in command.length..11) {
out.write(0x0)
}
val payloadBytes = Encode.bytes(item.payload)
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
// larger than this.
Encode.int32(payloadBytes.size, out)
// checksum
out.write(getChecksum(payloadBytes))
// message payload
out.write(payloadBytes)
}
val payloadBytes = Encode.bytes(payload)
/**
* A more efficient implementation of the write method, writing header data to the provided buffer and returning
* a new buffer containing the payload.
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
// larger than this.
Encode.int32(payloadBytes.size, out)
// checksum
out.write(getChecksum(payloadBytes))
// message payload
out.write(payloadBytes)
}
/**
* A more efficient implementation of the write method, writing header data to the provided buffer and returning
* a new buffer containing the payload.
* @param headerBuffer where the header data is written to (24 bytes)
* *
* @return a buffer containing the payload, ready to be read.
*/
fun writeHeaderAndGetPayloadBuffer(headerBuffer: ByteBuffer): ByteBuffer {
return ByteBuffer.wrap(writeHeader(headerBuffer))
}
/**
* For improved memory efficiency, you should use [.writeHeaderAndGetPayloadBuffer]
* and write the header buffer as well as the returned payload buffer into the channel.
* @param buffer where everything gets written to. Needs to be large enough for the whole message
* * to be written.
*/
override fun write(buffer: ByteBuffer) {
val payloadBytes = writeHeader(buffer)
buffer.put(payloadBytes)
}
private fun writeHeader(out: ByteBuffer): ByteArray {
// magic
Encode.int32(MAGIC, out)
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
val command = payload.command.name.toLowerCase()
out.put(command.toByteArray(charset("ASCII")))
for (i in command.length..11) {
out.put(0.toByte())
* @param headerBuffer where the header data is written to (24 bytes)
* *
* @return a buffer containing the payload, ready to be read.
*/
fun writeHeaderAndGetPayloadBuffer(headerBuffer: ByteBuffer): ByteBuffer {
return ByteBuffer.wrap(writeHeader(headerBuffer))
}
val payloadBytes = Encode.bytes(payload)
/**
* For improved memory efficiency, you should use [.writeHeaderAndGetPayloadBuffer]
* and write the header buffer as well as the returned payload buffer into the channel.
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
// larger than this.
Encode.int32(payloadBytes.size, out)
* @param buffer where everything gets written to. Needs to be large enough for the whole message
* * to be written.
*/
override fun write(buffer: ByteBuffer) {
val payloadBytes = writeHeader(buffer)
buffer.put(payloadBytes)
}
// checksum
out.put(getChecksum(payloadBytes))
private fun writeHeader(out: ByteBuffer): ByteArray {
// magic
Encode.int32(MAGIC, out)
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
val command = item.payload.command.name.toLowerCase()
out.put(command.toByteArray(charset("ASCII")))
for (i in command.length..11) {
out.put(0.toByte())
}
val payloadBytes = Encode.bytes(item.payload)
// Length of payload in number of bytes. Because of other restrictions, there is no reason why this length would
// ever be larger than 1600003 bytes. Some clients include a sanity-check to avoid processing messages which are
// larger than this.
Encode.int32(payloadBytes.size, out)
// checksum
out.put(getChecksum(payloadBytes))
// message payload
return payloadBytes
}
/**
* First 4 bytes of sha512(payload)
*/
private fun getChecksum(bytes: ByteArray): ByteArray {
val d = cryptography().sha512(bytes)
return byteArrayOf(d[0], d[1], d[2], d[3])
}
// message payload
return payloadBytes
}
companion object {

View File

@ -81,8 +81,8 @@ data class ObjectMessage(
get() {
try {
val out = ByteArrayOutputStream()
writeHeaderWithoutNonce(out)
payload.writeBytesToSign(out)
writer.writeHeaderWithoutNonce(out)
payload.writer().writeBytesToSign(out)
return out.toByteArray()
} catch (e: IOException) {
throw ApplicationException(e)
@ -131,30 +131,39 @@ data class ObjectMessage(
return cryptography().isSignatureValid(bytesToSign, payload.signature ?: return false, pubkey)
}
override fun write(out: OutputStream) {
out.write(nonce ?: ByteArray(8))
out.write(payloadBytesWithoutNonce)
}
override fun write(buffer: ByteBuffer) {
buffer.put(nonce ?: ByteArray(8))
buffer.put(payloadBytesWithoutNonce)
}
private fun writeHeaderWithoutNonce(out: OutputStream) {
Encode.int64(expiresTime, out)
Encode.int32(type, out)
Encode.varInt(version, out)
Encode.varInt(stream, out)
}
val payloadBytesWithoutNonce: ByteArray by lazy {
val out = ByteArrayOutputStream()
writeHeaderWithoutNonce(out)
payload.write(out)
writer.writeHeaderWithoutNonce(out)
payload.writer().write(out)
out.toByteArray()
}
private val writer = Writer(this)
override fun writer(): StreamableWriter = writer
private class Writer(
private val item: ObjectMessage
) : StreamableWriter {
override fun write(out: OutputStream) {
out.write(item.nonce ?: ByteArray(8))
out.write(item.payloadBytesWithoutNonce)
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.nonce ?: ByteArray(8))
buffer.put(item.payloadBytesWithoutNonce)
}
internal fun writeHeaderWithoutNonce(out: OutputStream) {
Encode.int64(item.expiresTime, out)
Encode.int32(item.type, out)
Encode.varInt(item.version, out)
Encode.varInt(item.stream, out)
}
}
class Builder {
private var nonce: ByteArray? = null
private var expiresTime: Long = 0

View File

@ -260,102 +260,6 @@ class Plaintext private constructor(
id = builder.id
}
fun write(out: OutputStream, includeSignature: Boolean) {
Encode.varInt(from.version, out)
Encode.varInt(from.stream, out)
from.pubkey?.apply {
Encode.int32(behaviorBitfield, out)
out.write(signingKey, 1, 64)
out.write(encryptionKey, 1, 64)
if (from.version >= 3) {
Encode.varInt(nonceTrialsPerByte, out)
Encode.varInt(extraBytes, out)
}
} ?: {
Encode.int32(0, out)
val empty = ByteArray(64)
out.write(empty)
out.write(empty)
if (from.version >= 3) {
Encode.varInt(0, out)
Encode.varInt(0, out)
}
}.invoke()
if (type == MSG) {
out.write(to?.ripe ?: throw IllegalStateException("No recipient set for message"))
}
Encode.varInt(encodingCode, out)
Encode.varInt(message.size, out)
out.write(message)
if (type == MSG) {
if (to?.has(Feature.DOES_ACK) ?: false) {
val ack = ByteArrayOutputStream()
ackMessage?.write(ack)
Encode.varBytes(ack.toByteArray(), out)
} else {
Encode.varInt(0, out)
}
}
if (includeSignature) {
if (signature == null) {
Encode.varInt(0, out)
} else {
Encode.varBytes(signature!!, out)
}
}
}
fun write(buffer: ByteBuffer, includeSignature: Boolean) {
Encode.varInt(from.version, buffer)
Encode.varInt(from.stream, buffer)
if (from.pubkey == null) {
Encode.int32(0, buffer)
val empty = ByteArray(64)
buffer.put(empty)
buffer.put(empty)
if (from.version >= 3) {
Encode.varInt(0, buffer)
Encode.varInt(0, buffer)
}
} else {
Encode.int32(from.pubkey!!.behaviorBitfield, buffer)
buffer.put(from.pubkey!!.signingKey, 1, 64)
buffer.put(from.pubkey!!.encryptionKey, 1, 64)
if (from.version >= 3) {
Encode.varInt(from.pubkey!!.nonceTrialsPerByte, buffer)
Encode.varInt(from.pubkey!!.extraBytes, buffer)
}
}
if (type == MSG) {
buffer.put(to!!.ripe)
}
Encode.varInt(encodingCode, buffer)
Encode.varBytes(message, buffer)
if (type == MSG) {
if (to!!.has(Feature.DOES_ACK) && ackMessage != null) {
Encode.varBytes(Encode.bytes(ackMessage!!), buffer)
} else {
Encode.varInt(0, buffer)
}
}
if (includeSignature) {
val sig = signature
if (sig == null) {
Encode.varInt(0, buffer)
} else {
Encode.varBytes(sig, buffer)
}
}
}
override fun write(out: OutputStream) {
write(out, true)
}
override fun write(buffer: ByteBuffer) {
write(buffer, true)
}
fun updateNextTry() {
if (to != null) {
if (nextTry == null) {
@ -484,6 +388,7 @@ class Plaintext private constructor(
}
enum class Encoding constructor(code: Long) {
IGNORE(0), TRIVIAL(1), SIMPLE(2), EXTENDED(3);
var code: Long = 0
@ -495,7 +400,8 @@ class Plaintext private constructor(
companion object {
@JvmStatic fun fromCode(code: Long): Encoding? {
@JvmStatic
fun fromCode(code: Long): Encoding? {
for (e in values()) {
if (e.code == code) {
return e
@ -503,12 +409,13 @@ class Plaintext private constructor(
}
return null
}
}
}
enum class Status {
DRAFT,
// For sent messages
PUBKEY_REQUESTED,
DOING_PROOF_OF_WORK,
SENT,
@ -517,9 +424,110 @@ class Plaintext private constructor(
}
enum class Type {
MSG, BROADCAST
}
fun writer(includeSignature: Boolean): StreamableWriter = Writer(this, includeSignature)
override fun writer(): StreamableWriter = Writer(this)
private class Writer(
private val item: Plaintext,
private val includeSignature: Boolean = true
) : StreamableWriter {
override fun write(out: OutputStream) {
Encode.varInt(item.from.version, out)
Encode.varInt(item.from.stream, out)
item.from.pubkey?.apply {
Encode.int32(behaviorBitfield, out)
out.write(signingKey, 1, 64)
out.write(encryptionKey, 1, 64)
if (item.from.version >= 3) {
Encode.varInt(nonceTrialsPerByte, out)
Encode.varInt(extraBytes, out)
}
} ?: {
Encode.int32(0, out)
val empty = ByteArray(64)
out.write(empty)
out.write(empty)
if (item.from.version >= 3) {
Encode.varInt(0, out)
Encode.varInt(0, out)
}
}.invoke()
if (item.type == MSG) {
out.write(item.to?.ripe ?: throw IllegalStateException("No recipient set for message"))
}
Encode.varInt(item.encodingCode, out)
Encode.varInt(item.message.size, out)
out.write(item.message)
if (item.type == MSG) {
if (item.to?.has(Feature.DOES_ACK) == true) {
val ack = ByteArrayOutputStream()
item.ackMessage?.writer()?.write(ack)
Encode.varBytes(ack.toByteArray(), out)
} else {
Encode.varInt(0, out)
}
}
if (includeSignature) {
val sig = item.signature
if (sig == null) {
Encode.varInt(0, out)
} else {
Encode.varBytes(sig, out)
}
}
}
override fun write(buffer: ByteBuffer) {
Encode.varInt(item.from.version, buffer)
Encode.varInt(item.from.stream, buffer)
if (item.from.pubkey == null) {
Encode.int32(0, buffer)
val empty = ByteArray(64)
buffer.put(empty)
buffer.put(empty)
if (item.from.version >= 3) {
Encode.varInt(0, buffer)
Encode.varInt(0, buffer)
}
} else {
Encode.int32(item.from.pubkey!!.behaviorBitfield, buffer)
buffer.put(item.from.pubkey!!.signingKey, 1, 64)
buffer.put(item.from.pubkey!!.encryptionKey, 1, 64)
if (item.from.version >= 3) {
Encode.varInt(item.from.pubkey!!.nonceTrialsPerByte, buffer)
Encode.varInt(item.from.pubkey!!.extraBytes, buffer)
}
}
if (item.type == MSG) {
buffer.put(item.to!!.ripe)
}
Encode.varInt(item.encodingCode, buffer)
Encode.varBytes(item.message, buffer)
if (item.type == MSG) {
if (item.to!!.has(Feature.DOES_ACK) && item.ackMessage != null) {
Encode.varBytes(Encode.bytes(item.ackMessage!!), buffer)
} else {
Encode.varInt(0, buffer)
}
}
if (includeSignature) {
val sig = item.signature
if (sig == null) {
Encode.varInt(0, buffer)
} else {
Encode.varBytes(sig, buffer)
}
}
}
}
class Builder(internal val type: Type) {
internal var id: Any? = null
internal var inventoryVector: InventoryVector? = null
@ -742,14 +750,16 @@ class Plaintext private constructor(
companion object {
@JvmStatic fun read(type: Type, `in`: InputStream): Plaintext {
@JvmStatic
fun read(type: Type, `in`: InputStream): Plaintext {
return readWithoutSignature(type, `in`)
.signature(Decode.varBytes(`in`))
.received(UnixTime.now)
.build()
}
@JvmStatic fun readWithoutSignature(type: Type, `in`: InputStream): Plaintext.Builder {
@JvmStatic
fun readWithoutSignature(type: Type, `in`: InputStream): Plaintext.Builder {
val version = Decode.varInt(`in`)
return Builder(type)
.addressVersion(version)

View File

@ -24,7 +24,27 @@ import java.nio.ByteBuffer
* An object that can be written to an [OutputStream]
*/
interface Streamable : Serializable {
fun write(out: OutputStream)
fun writer(): StreamableWriter
}
interface SignedStreamable : Streamable {
override fun writer(): SignedStreamableWriter
}
interface EncryptedStreamable : SignedStreamable {
override fun writer(): EncryptedStreamableWriter
}
interface StreamableWriter: Serializable {
fun write(out: OutputStream)
fun write(buffer: ByteBuffer)
}
interface SignedStreamableWriter : StreamableWriter {
fun writeBytesToSign(out: OutputStream)
}
interface EncryptedStreamableWriter : SignedStreamableWriter {
fun writeUnencrypted(out: OutputStream)
fun writeUnencrypted(buffer: ByteBuffer)
}

View File

@ -26,12 +26,12 @@ class VerAck : MessagePayload {
override val command: MessagePayload.Command = MessagePayload.Command.VERACK
override fun write(out: OutputStream) {
// 'verack' doesn't have any payload, so there is nothing to write
}
// 'verack' doesn't have any payload, so there is nothing to write
override fun writer(): StreamableWriter = EmptyWriter
override fun write(buffer: ByteBuffer) {
// 'verack' doesn't have any payload, so there is nothing to write
internal object EmptyWriter : StreamableWriter {
override fun write(out: OutputStream) = Unit
override fun write(buffer: ByteBuffer) = Unit
}
companion object {

View File

@ -71,32 +71,36 @@ class Version constructor(
val streams: LongArray = longArrayOf(1)
) : MessagePayload {
fun provides(service: Service?): Boolean {
return service != null && service.isEnabled(services)
}
fun provides(service: Service?) = service?.isEnabled(services) == true
override val command: MessagePayload.Command = MessagePayload.Command.VERSION
override fun write(out: OutputStream) {
Encode.int32(version, out)
Encode.int64(services, out)
Encode.int64(timestamp, out)
addrRecv.write(out, true)
addrFrom.write(out, true)
Encode.int64(nonce, out)
Encode.varString(userAgent, out)
Encode.varIntList(streams, out)
}
override fun writer(): StreamableWriter = Writer(this)
override fun write(buffer: ByteBuffer) {
Encode.int32(version, buffer)
Encode.int64(services, buffer)
Encode.int64(timestamp, buffer)
addrRecv.write(buffer, true)
addrFrom.write(buffer, true)
Encode.int64(nonce, buffer)
Encode.varString(userAgent, buffer)
Encode.varIntList(streams, buffer)
private class Writer(
private val item: Version
) : StreamableWriter {
override fun write(out: OutputStream) {
Encode.int32(item.version, out)
Encode.int64(item.services, out)
Encode.int64(item.timestamp, out)
item.addrRecv.writer(true).write(out)
item.addrFrom.writer(true).write(out)
Encode.int64(item.nonce, out)
Encode.varString(item.userAgent, out)
Encode.varIntList(item.streams, out)
}
override fun write(buffer: ByteBuffer) {
Encode.int32(item.version, buffer)
Encode.int64(item.services, buffer)
Encode.int64(item.timestamp, buffer)
item.addrRecv.writer(true).write(buffer)
item.addrFrom.writer(true).write(buffer)
Encode.int64(item.nonce, buffer)
Encode.varString(item.userAgent, buffer)
Encode.varIntList(item.streams, buffer)
}
}
class Builder {
@ -187,9 +191,7 @@ class Version constructor(
// TODO: NODE_SSL(2);
NODE_NETWORK(1);
fun isEnabled(flag: Long): Boolean {
return (flag and this.flag) != 0L
}
fun isEnabled(flag: Long) = (flag and this.flag) != 0L
companion object {
fun getServiceFlag(vararg services: Service): Long {

View File

@ -29,7 +29,12 @@ import java.util.*
* Users who are subscribed to the sending address will see the message appear in their inbox.
* Broadcasts are version 4 or 5.
*/
abstract class Broadcast protected constructor(version: Long, override val stream: Long, protected var encrypted: CryptoBox?, override var plaintext: Plaintext?) : ObjectPayload(version), Encrypted, PlaintextHolder {
abstract class Broadcast protected constructor(
version: Long,
override val stream: Long,
protected var encrypted: CryptoBox?,
override var plaintext: Plaintext?
) : ObjectPayload(version), Encrypted, PlaintextHolder {
override val isSigned: Boolean = true

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.Streamable
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.entity.valueobject.PrivateKey.Companion.PRIVATE_KEY_SIZE
import ch.dissem.bitmessage.exception.DecryptionFailedException
import ch.dissem.bitmessage.utils.*
@ -108,44 +109,53 @@ class CryptoBox : Streamable {
private fun calculateMac(key_m: ByteArray): ByteArray {
val macData = ByteArrayOutputStream()
writeWithoutMAC(macData)
writer.writeWithoutMAC(macData)
return cryptography().mac(key_m, macData.toByteArray())
}
private fun writeWithoutMAC(out: OutputStream) {
out.write(initializationVector)
Encode.int16(curveType, out)
writeCoordinateComponent(out, Points.getX(R))
writeCoordinateComponent(out, Points.getY(R))
out.write(encrypted)
}
private val writer = Writer(this)
override fun writer(): StreamableWriter = writer
private fun writeCoordinateComponent(out: OutputStream, x: ByteArray) {
val offset = Bytes.numberOfLeadingZeros(x)
val length = x.size - offset
Encode.int16(length, out)
out.write(x, offset, length)
}
private class Writer(
private val item: CryptoBox
) : StreamableWriter {
private fun writeCoordinateComponent(buffer: ByteBuffer, x: ByteArray) {
val offset = Bytes.numberOfLeadingZeros(x)
val length = x.size - offset
Encode.int16(length, buffer)
buffer.put(x, offset, length)
}
override fun write(out: OutputStream) {
writeWithoutMAC(out)
out.write(item.mac)
}
override fun write(out: OutputStream) {
writeWithoutMAC(out)
out.write(mac)
}
internal fun writeWithoutMAC(out: OutputStream) {
out.write(item.initializationVector)
Encode.int16(item.curveType, out)
writeCoordinateComponent(out, Points.getX(item.R))
writeCoordinateComponent(out, Points.getY(item.R))
out.write(item.encrypted)
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.initializationVector)
Encode.int16(item.curveType, buffer)
writeCoordinateComponent(buffer, Points.getX(item.R))
writeCoordinateComponent(buffer, Points.getY(item.R))
buffer.put(item.encrypted)
buffer.put(item.mac)
}
private fun writeCoordinateComponent(out: OutputStream, x: ByteArray) {
val offset = Bytes.numberOfLeadingZeros(x)
val length = x.size - offset
Encode.int16(length, out)
out.write(x, offset, length)
}
private fun writeCoordinateComponent(buffer: ByteBuffer, x: ByteArray) {
val offset = Bytes.numberOfLeadingZeros(x)
val length = x.size - offset
Encode.int16(length, buffer)
buffer.put(x, offset, length)
}
override fun write(buffer: ByteBuffer) {
buffer.put(initializationVector)
Encode.int16(curveType, buffer)
writeCoordinateComponent(buffer, Points.getX(R))
writeCoordinateComponent(buffer, Points.getY(R))
buffer.put(encrypted)
buffer.put(mac)
}
class Builder {
@ -187,15 +197,14 @@ class CryptoBox : Streamable {
return this
}
fun build(): CryptoBox {
return CryptoBox(this)
}
fun build() = CryptoBox(this)
}
companion object {
private val LOG = LoggerFactory.getLogger(CryptoBox::class.java)
@JvmStatic fun read(stream: InputStream, length: Int): CryptoBox {
@JvmStatic
fun read(stream: InputStream, length: Int): CryptoBox {
val counter = AccessCounter()
return Builder()
.IV(Decode.bytes(stream, 16, counter))

View File

@ -16,6 +16,7 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.SignedStreamableWriter
import ch.dissem.bitmessage.utils.Decode
import java.io.InputStream
import java.io.OutputStream
@ -30,14 +31,6 @@ class GenericPayload(version: Long, override val stream: Long, val data: ByteArr
override val type: ObjectType? = null
override fun write(out: OutputStream) {
out.write(data)
}
override fun write(buffer: ByteBuffer) {
buffer.put(data)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is GenericPayload) return false
@ -52,9 +45,27 @@ class GenericPayload(version: Long, override val stream: Long, val data: ByteArr
return result
}
companion object {
@JvmStatic fun read(version: Long, stream: Long, `is`: InputStream, length: Int): GenericPayload {
return GenericPayload(version, stream, Decode.bytes(`is`, length))
override fun writer(): SignedStreamableWriter = Writer(this)
private class Writer(
private val item: GenericPayload
) : SignedStreamableWriter {
override fun write(out: OutputStream) {
out.write(item.data)
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.data)
}
override fun writeBytesToSign(out: OutputStream) = Unit // nothing to do
}
companion object {
@JvmStatic
fun read(version: Long, stream: Long, `is`: InputStream, length: Int) =
GenericPayload(version, stream, Decode.bytes(`is`, length))
}
}

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.SignedStreamableWriter
import ch.dissem.bitmessage.utils.Decode
import java.io.InputStream
import java.io.OutputStream
@ -47,16 +48,27 @@ class GetPubkey : ObjectPayload {
this.ripeTag = ripeOrTag
}
override fun write(out: OutputStream) {
out.write(ripeTag)
}
override fun writer(): SignedStreamableWriter = Writer(this)
private class Writer(
private val item: GetPubkey
) : SignedStreamableWriter {
override fun write(out: OutputStream) {
out.write(item.ripeTag)
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.ripeTag)
}
override fun writeBytesToSign(out: OutputStream) = Unit // nothing to sign
override fun write(buffer: ByteBuffer) {
buffer.put(ripeTag)
}
companion object {
@JvmStatic fun read(`is`: InputStream, stream: Long, length: Int, version: Long): GetPubkey {
@JvmStatic
fun read(`is`: InputStream, stream: Long, length: Int, version: Long): GetPubkey {
return GetPubkey(version, stream, Decode.bytes(`is`, length))
}
}

View File

@ -16,10 +16,8 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.Encrypted
import ch.dissem.bitmessage.entity.Plaintext
import ch.dissem.bitmessage.entity.*
import ch.dissem.bitmessage.entity.Plaintext.Type.MSG
import ch.dissem.bitmessage.entity.PlaintextHolder
import ch.dissem.bitmessage.exception.DecryptionFailedException
import java.io.InputStream
import java.io.OutputStream
@ -51,10 +49,6 @@ class Msg : ObjectPayload, Encrypted, PlaintextHolder {
override val isSigned: Boolean = true
override fun writeBytesToSign(out: OutputStream) {
plaintext?.write(out, false) ?: throw IllegalStateException("no plaintext data available")
}
override var signature: ByteArray?
get() = plaintext?.signature
set(signature) {
@ -73,14 +67,6 @@ class Msg : ObjectPayload, Encrypted, PlaintextHolder {
override val isDecrypted: Boolean
get() = plaintext != null
override fun write(out: OutputStream) {
encrypted?.write(out) ?: throw IllegalStateException("Msg must be signed and encrypted before writing it.")
}
override fun write(buffer: ByteBuffer) {
encrypted?.write(buffer) ?: throw IllegalStateException("Msg must be signed and encrypted before writing it.")
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Msg) return false
@ -89,14 +75,35 @@ class Msg : ObjectPayload, Encrypted, PlaintextHolder {
return stream == other.stream && (encrypted == other.encrypted || plaintext == other.plaintext)
}
override fun hashCode(): Int {
return stream.toInt()
override fun hashCode() = stream.toInt()
override fun writer(): SignedStreamableWriter = Writer(this)
private class Writer(
private val item: Msg
) : SignedStreamableWriter {
val encryptedDataWriter = item.encrypted?.writer()
override fun write(out: OutputStream) {
encryptedDataWriter?.write(out) ?: throw IllegalStateException("Msg must be signed and encrypted before writing it.")
}
override fun write(buffer: ByteBuffer) {
encryptedDataWriter?.write(buffer) ?: throw IllegalStateException("Msg must be signed and encrypted before writing it.")
}
override fun writeBytesToSign(out: OutputStream) {
item.plaintext?.writer(false)?.write(out) ?: throw IllegalStateException("no plaintext data available")
}
}
companion object {
val ACK_LENGTH = 32
@JvmStatic fun read(`in`: InputStream, stream: Long, length: Int): Msg {
@JvmStatic
fun read(`in`: InputStream, stream: Long, length: Int): Msg {
return Msg(stream, CryptoBox.read(`in`, length))
}
}

View File

@ -17,13 +17,14 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.ObjectMessage
import ch.dissem.bitmessage.entity.SignedStreamable
import ch.dissem.bitmessage.entity.Streamable
import java.io.OutputStream
/**
* The payload of an 'object' command. This is shared by the network.
*/
abstract class ObjectPayload protected constructor(val version: Long) : Streamable {
abstract class ObjectPayload protected constructor(val version: Long) : SignedStreamable {
abstract val type: ObjectType?
@ -31,10 +32,6 @@ abstract class ObjectPayload protected constructor(val version: Long) : Streamab
open val isSigned: Boolean = false
open fun writeBytesToSign(out: OutputStream) {
// nothing to do
}
/**
* @return the ECDSA signature which, as of protocol v3, covers the object header starting with the time,
* * appended with the data described in this table down to the extra_bytes. Therefore, this must

View File

@ -18,6 +18,8 @@ package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.InternalContext.Companion.NETWORK_EXTRA_BYTES
import ch.dissem.bitmessage.InternalContext.Companion.NETWORK_NONCE_TRIALS_PER_BYTE
import ch.dissem.bitmessage.entity.EncryptedStreamableWriter
import ch.dissem.bitmessage.entity.SignedStreamableWriter
import ch.dissem.bitmessage.utils.Singleton.cryptography
import java.io.OutputStream
import java.nio.ByteBuffer
@ -42,13 +44,7 @@ abstract class Pubkey protected constructor(version: Long) : ObjectPayload(versi
open val extraBytes: Long = NETWORK_EXTRA_BYTES
open fun writeUnencrypted(out: OutputStream) {
write(out)
}
open fun writeUnencrypted(buffer: ByteBuffer) {
write(buffer)
}
abstract override fun writer(): EncryptedStreamableWriter
/**
* Bits 0 through 29 are yet undefined

View File

@ -16,6 +16,7 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.EncryptedStreamableWriter
import ch.dissem.bitmessage.utils.Decode
import ch.dissem.bitmessage.utils.Encode
import java.io.InputStream
@ -25,21 +26,47 @@ import java.nio.ByteBuffer
/**
* A version 2 public key.
*/
open class V2Pubkey constructor(version: Long, override val stream: Long, override val behaviorBitfield: Int, signingKey: ByteArray, encryptionKey: ByteArray) : Pubkey(version) {
open class V2Pubkey constructor(
version: Long,
override val stream: Long,
override val behaviorBitfield: Int,
signingKey: ByteArray,
encryptionKey: ByteArray
) : Pubkey(version) {
override val signingKey: ByteArray = if (signingKey.size == 64) add0x04(signingKey) else signingKey
override val encryptionKey: ByteArray = if (encryptionKey.size == 64) add0x04(encryptionKey) else encryptionKey
override fun write(out: OutputStream) {
Encode.int32(behaviorBitfield, out)
out.write(signingKey, 1, 64)
out.write(encryptionKey, 1, 64)
}
override fun writer(): EncryptedStreamableWriter = Writer(this)
protected open class Writer(
private val item: V2Pubkey
) : EncryptedStreamableWriter {
override fun write(out: OutputStream) {
Encode.int32(item.behaviorBitfield, out)
out.write(item.signingKey, 1, 64)
out.write(item.encryptionKey, 1, 64)
}
override fun write(buffer: ByteBuffer) {
Encode.int32(item.behaviorBitfield, buffer)
buffer.put(item.signingKey, 1, 64)
buffer.put(item.encryptionKey, 1, 64)
}
override fun writeBytesToSign(out: OutputStream) {
// Nothing to do
}
override fun writeUnencrypted(out: OutputStream) {
write(out)
}
override fun writeUnencrypted(buffer: ByteBuffer) {
write(buffer)
}
override fun write(buffer: ByteBuffer) {
Encode.int32(behaviorBitfield, buffer)
buffer.put(signingKey, 1, 64)
buffer.put(encryptionKey, 1, 64)
}
class Builder {

View File

@ -16,6 +16,9 @@
package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.EncryptedStreamableWriter
import ch.dissem.bitmessage.entity.SignedStreamableWriter
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.utils.Decode
import ch.dissem.bitmessage.utils.Encode
import java.io.InputStream
@ -34,32 +37,8 @@ class V3Pubkey protected constructor(
override var signature: ByteArray? = null
) : V2Pubkey(version, stream, behaviorBitfield, signingKey, encryptionKey) {
override fun write(out: OutputStream) {
writeBytesToSign(out)
Encode.varBytes(
signature ?: throw IllegalStateException("signature not available"),
out
)
}
override fun write(buffer: ByteBuffer) {
super.write(buffer)
Encode.varInt(nonceTrialsPerByte, buffer)
Encode.varInt(extraBytes, buffer)
Encode.varBytes(
signature ?: throw IllegalStateException("signature not available"),
buffer
)
}
override val isSigned: Boolean = true
override fun writeBytesToSign(out: OutputStream) {
super.write(out)
Encode.varInt(nonceTrialsPerByte, out)
Encode.varInt(extraBytes, out)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is V3Pubkey) return false
@ -75,6 +54,37 @@ class V3Pubkey protected constructor(
return Objects.hash(nonceTrialsPerByte, extraBytes)
}
override fun writer(): EncryptedStreamableWriter = Writer(this)
protected open class Writer(
private val item: V3Pubkey
) : V2Pubkey.Writer(item) {
override fun write(out: OutputStream) {
writeBytesToSign(out)
Encode.varBytes(
item.signature ?: throw IllegalStateException("signature not available"),
out
)
}
override fun write(buffer: ByteBuffer) {
super.write(buffer)
Encode.varInt(item.nonceTrialsPerByte, buffer)
Encode.varInt(item.extraBytes, buffer)
Encode.varBytes(
item.signature ?: throw IllegalStateException("signature not available"),
buffer
)
}
override fun writeBytesToSign(out: OutputStream) {
super.write(out)
Encode.varInt(item.nonceTrialsPerByte, out)
Encode.varInt(item.extraBytes, out)
}
}
class Builder {
private var streamNumber: Long = 0
private var behaviorBitfield: Int = 0

View File

@ -18,7 +18,7 @@ package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.Plaintext
import ch.dissem.bitmessage.entity.SignedStreamableWriter
import java.io.InputStream
import java.io.OutputStream
import java.nio.ByteBuffer
@ -38,21 +38,28 @@ open class V4Broadcast : Broadcast {
throw IllegalArgumentException("Address version 3 or older expected, but was " + senderAddress.version)
}
override fun writer(): SignedStreamableWriter = Writer(this)
override fun writeBytesToSign(out: OutputStream) {
plaintext?.write(out, false) ?: throw IllegalStateException("no plaintext data available")
}
protected open class Writer(
private val item: V4Broadcast
) : SignedStreamableWriter {
override fun write(out: OutputStream) {
encrypted?.write(out) ?: throw IllegalStateException("broadcast not encrypted")
}
override fun writeBytesToSign(out: OutputStream) {
item.plaintext?.writer(false)?.write(out) ?: throw IllegalStateException("no plaintext data available")
}
override fun write(buffer: ByteBuffer) {
encrypted?.write(buffer) ?: throw IllegalStateException("broadcast not encrypted")
override fun write(out: OutputStream) {
item.encrypted?.writer()?.write(out) ?: throw IllegalStateException("broadcast not encrypted")
}
override fun write(buffer: ByteBuffer) {
item.encrypted?.writer()?.write(buffer) ?: throw IllegalStateException("broadcast not encrypted")
}
}
companion object {
@JvmStatic fun read(`in`: InputStream, stream: Long, length: Int): V4Broadcast {
@JvmStatic
fun read(`in`: InputStream, stream: Long, length: Int): V4Broadcast {
return V4Broadcast(4, stream, CryptoBox.read(`in`, length), null)
}
}

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.entity.payload
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.Encrypted
import ch.dissem.bitmessage.entity.EncryptedStreamableWriter
import ch.dissem.bitmessage.exception.DecryptionFailedException
import ch.dissem.bitmessage.utils.Decode
import java.io.InputStream
@ -63,29 +64,6 @@ class V4Pubkey : Pubkey, Encrypted {
override val isDecrypted: Boolean
get() = decrypted != null
override fun write(out: OutputStream) {
out.write(tag)
encrypted?.write(out) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun write(buffer: ByteBuffer) {
buffer.put(tag)
encrypted?.write(buffer) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun writeUnencrypted(out: OutputStream) {
decrypted?.write(out) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun writeUnencrypted(buffer: ByteBuffer) {
decrypted?.write(buffer) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun writeBytesToSign(out: OutputStream) {
out.write(tag)
decrypted?.writeBytesToSign(out) ?: throw IllegalStateException("pubkey is encrypted")
}
override val signingKey: ByteArray
get() = decrypted?.signingKey ?: throw IllegalStateException("pubkey is encrypted")
@ -126,8 +104,40 @@ class V4Pubkey : Pubkey, Encrypted {
return result
}
override fun writer(): EncryptedStreamableWriter = Writer(this)
private class Writer(
val item: V4Pubkey
) : EncryptedStreamableWriter {
override fun write(out: OutputStream) {
out.write(item.tag)
item.encrypted?.writer()?.write(out) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.tag)
item.encrypted?.writer()?.write(buffer) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun writeUnencrypted(out: OutputStream) {
item.decrypted?.writer()?.write(out) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun writeUnencrypted(buffer: ByteBuffer) {
item.decrypted?.writer()?.write(buffer) ?: throw IllegalStateException("pubkey is encrypted")
}
override fun writeBytesToSign(out: OutputStream) {
out.write(item.tag)
item.decrypted?.writer()?.writeBytesToSign(out) ?: throw IllegalStateException("pubkey is encrypted")
}
}
companion object {
@JvmStatic fun read(`in`: InputStream, stream: Long, length: Int, encrypted: Boolean): V4Pubkey {
@JvmStatic
fun read(`in`: InputStream, stream: Long, length: Int, encrypted: Boolean): V4Pubkey {
if (encrypted)
return V4Pubkey(stream,
Decode.bytes(`in`, 32),

View File

@ -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.entity.SignedStreamableWriter
import ch.dissem.bitmessage.utils.Decode
import java.io.InputStream
@ -40,18 +41,27 @@ class V5Broadcast : V4Broadcast {
this.tag = senderAddress.tag ?: throw IllegalStateException("version 4 address without tag")
}
override fun writeBytesToSign(out: OutputStream) {
out.write(tag)
super.writeBytesToSign(out)
}
override fun writer(): SignedStreamableWriter = Writer(this)
private class Writer(
private val item: V5Broadcast
) : V4Broadcast.Writer(item) {
override fun writeBytesToSign(out: OutputStream) {
out.write(item.tag)
super.writeBytesToSign(out)
}
override fun write(out: OutputStream) {
out.write(item.tag)
super.write(out)
}
override fun write(out: OutputStream) {
out.write(tag)
super.write(out)
}
companion object {
@JvmStatic fun read(`is`: InputStream, stream: Long, length: Int): V5Broadcast {
@JvmStatic
fun read(`is`: InputStream, stream: Long, length: Int): V5Broadcast {
return V5Broadcast(stream, Decode.bytes(`is`, 32), CryptoBox.read(`is`, length - 32))
}
}

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.entity.valueobject
import ch.dissem.bitmessage.entity.Streamable
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.utils.Strings
import java.io.OutputStream
import java.nio.ByteBuffer
@ -39,20 +40,29 @@ data class InventoryVector constructor(
return Arrays.hashCode(hash)
}
override fun write(out: OutputStream) {
out.write(hash)
}
override fun write(buffer: ByteBuffer) {
buffer.put(hash)
}
override fun toString(): String {
return Strings.hex(hash)
}
override fun writer(): StreamableWriter = Writer(this)
private class Writer(
private val item: InventoryVector
) : StreamableWriter {
override fun write(out: OutputStream) {
out.write(item.hash)
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.hash)
}
}
companion object {
@JvmStatic fun fromHash(hash: ByteArray?): InventoryVector? {
@JvmStatic
fun fromHash(hash: ByteArray?): InventoryVector? {
return InventoryVector(
hash ?: return null
)

View File

@ -17,6 +17,7 @@
package ch.dissem.bitmessage.entity.valueobject
import ch.dissem.bitmessage.entity.Streamable
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.entity.Version
import ch.dissem.bitmessage.utils.Encode
import ch.dissem.bitmessage.utils.UnixTime
@ -77,9 +78,7 @@ data class NetworkAddress(
fun provides(service: Version.Service?): Boolean = service?.isEnabled(services) ?: false
fun toInetAddress(): InetAddress {
return InetAddress.getByAddress(IPv6)
}
fun toInetAddress() = InetAddress.getByAddress(IPv6)
override fun equals(other: Any?): Boolean {
if (this === other) return true
@ -98,32 +97,40 @@ data class NetworkAddress(
return "[" + toInetAddress() + "]:" + port
}
override fun write(out: OutputStream) {
write(out, false)
}
fun writer(light: Boolean): StreamableWriter = Writer(
item = this,
light = light
)
fun write(out: OutputStream, light: Boolean) {
if (!light) {
Encode.int64(time, out)
Encode.int32(stream, out)
override fun writer(): StreamableWriter = Writer(
item = this
)
private class Writer(
private val item: NetworkAddress,
private val light: Boolean = false
) : StreamableWriter {
override fun write(out: OutputStream) {
if (!light) {
Encode.int64(item.time, out)
Encode.int32(item.stream, out)
}
Encode.int64(item.services, out)
out.write(item.IPv6)
Encode.int16(item.port, out)
}
Encode.int64(services, out)
out.write(IPv6)
Encode.int16(port, out)
}
override fun write(buffer: ByteBuffer) {
write(buffer, false)
}
fun write(buffer: ByteBuffer, light: Boolean) {
if (!light) {
Encode.int64(time, buffer)
Encode.int32(stream, buffer)
override fun write(buffer: ByteBuffer) {
if (!light) {
Encode.int64(item.time, buffer)
Encode.int32(item.stream, buffer)
}
Encode.int64(item.services, buffer)
buffer.put(item.IPv6)
Encode.int16(item.port, buffer)
}
Encode.int64(services, buffer)
buffer.put(IPv6)
Encode.int16(port, buffer)
}
class Builder {
@ -194,6 +201,7 @@ data class NetworkAddress(
}
companion object {
@JvmField val ANY = NetworkAddress(time = 0, stream = 0, services = 0, IPv6 = ByteArray(16), port = 0)
@JvmField
val ANY = NetworkAddress(time = 0, stream = 0, services = 0, IPv6 = ByteArray(16), port = 0)
}
}

View File

@ -19,6 +19,7 @@ package ch.dissem.bitmessage.entity.valueobject
import ch.dissem.bitmessage.InternalContext
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.Streamable
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.entity.payload.Pubkey
import ch.dissem.bitmessage.exception.ApplicationException
import ch.dissem.bitmessage.factory.Factory
@ -66,7 +67,52 @@ data class PrivateKey(
builder.nonceTrialsPerByte, builder.extraBytes, *builder.features)
)
private class Builder internal constructor(internal val version: Long, internal val stream: Long, internal val shorter: Boolean) {
override fun equals(other: Any?) = other is PrivateKey
&& Arrays.equals(privateEncryptionKey, other.privateEncryptionKey)
&& Arrays.equals(privateSigningKey, other.privateSigningKey)
&& pubkey == other.pubkey
override fun hashCode() = pubkey.hashCode()
override fun writer(): StreamableWriter = Writer(this)
private class Writer(
private val item: PrivateKey
) : StreamableWriter {
override fun write(out: OutputStream) {
Encode.varInt(item.pubkey.version, out)
Encode.varInt(item.pubkey.stream, out)
val baos = ByteArrayOutputStream()
item.pubkey.writer().writeUnencrypted(baos)
Encode.varInt(baos.size(), out)
out.write(baos.toByteArray())
Encode.varBytes(item.privateSigningKey, out)
Encode.varBytes(item.privateEncryptionKey, out)
}
override fun write(buffer: ByteBuffer) {
Encode.varInt(item.pubkey.version, buffer)
Encode.varInt(item.pubkey.stream, buffer)
try {
val baos = ByteArrayOutputStream()
item.pubkey.writer().writeUnencrypted(baos)
Encode.varBytes(baos.toByteArray(), buffer)
} catch (e: IOException) {
throw ApplicationException(e)
}
Encode.varBytes(item.privateSigningKey, buffer)
Encode.varBytes(item.privateEncryptionKey, buffer)
}
}
private class Builder internal constructor(
internal val version: Long,
internal val stream: Long,
internal val shorter: Boolean
) {
internal var seed: ByteArray? = null
internal var nextNonce: Long = 0
@ -129,44 +175,12 @@ data class PrivateKey(
}
}
override fun write(out: OutputStream) {
Encode.varInt(pubkey.version, out)
Encode.varInt(pubkey.stream, out)
val baos = ByteArrayOutputStream()
pubkey.writeUnencrypted(baos)
Encode.varInt(baos.size(), out)
out.write(baos.toByteArray())
Encode.varBytes(privateSigningKey, out)
Encode.varBytes(privateEncryptionKey, out)
}
override fun write(buffer: ByteBuffer) {
Encode.varInt(pubkey.version, buffer)
Encode.varInt(pubkey.stream, buffer)
try {
val baos = ByteArrayOutputStream()
pubkey.writeUnencrypted(baos)
Encode.varBytes(baos.toByteArray(), buffer)
} catch (e: IOException) {
throw ApplicationException(e)
}
Encode.varBytes(privateSigningKey, buffer)
Encode.varBytes(privateEncryptionKey, buffer)
}
override fun equals(other: Any?) = other is PrivateKey
&& Arrays.equals(privateEncryptionKey, other.privateEncryptionKey)
&& Arrays.equals(privateSigningKey, other.privateSigningKey)
&& pubkey == other.pubkey
override fun hashCode() = pubkey.hashCode()
companion object {
@JvmField val PRIVATE_KEY_SIZE = 32
@JvmField
val PRIVATE_KEY_SIZE = 32
@JvmStatic fun deterministic(passphrase: String, numberOfAddresses: Int, version: Long, stream: Long, shorter: Boolean): List<PrivateKey> {
@JvmStatic
fun deterministic(passphrase: String, numberOfAddresses: Int, version: Long, stream: Long, shorter: Boolean): List<PrivateKey> {
val result = ArrayList<PrivateKey>(numberOfAddresses)
val builder = Builder(version, stream, shorter).seed(passphrase)
for (i in 0..numberOfAddresses - 1) {
@ -176,7 +190,8 @@ data class PrivateKey(
return result
}
@JvmStatic fun read(`is`: InputStream): PrivateKey {
@JvmStatic
fun read(`is`: InputStream): PrivateKey {
val version = Decode.varInt(`is`).toInt()
val stream = Decode.varInt(`is`)
val len = Decode.varInt(`is`).toInt()

View File

@ -45,25 +45,25 @@ data class Message constructor(
override fun pack(): MPMap<MPString, MPType<*>> {
val result = MPMap<MPString, MPType<*>>()
result.put(mp(""), mp(TYPE))
result.put(mp("subject"), mp(subject))
result.put(mp("body"), mp(body))
result.put("".mp, TYPE.mp)
result.put("subject".mp, subject.mp)
result.put("body".mp, body.mp)
if (!files.isEmpty()) {
val items = MPArray<MPMap<MPString, MPType<*>>>()
result.put(mp("files"), items)
result.put("files".mp, items)
for (file in files) {
val item = MPMap<MPString, MPType<*>>()
item.put(mp("name"), mp(file.name))
item.put(mp("data"), mp(*file.data))
item.put(mp("type"), mp(file.type))
item.put(mp("disposition"), mp(file.disposition.name))
item.put("name".mp, file.name.mp)
item.put("data".mp, file.data.mp)
item.put("type".mp, file.type.mp)
item.put("disposition".mp, file.disposition.name.mp)
items.add(item)
}
}
if (!parents.isEmpty()) {
val items = MPArray<MPBinary>()
result.put(mp("parents"), items)
result.put("parents".mp, items)
for ((hash) in parents) {
items.add(mp(*hash))
}
@ -139,26 +139,26 @@ data class Message constructor(
override val type: String = TYPE
override fun unpack(map: MPMap<MPString, MPType<*>>): Message {
val subject = str(map[mp("subject")]) ?: ""
val body = str(map[mp("body")]) ?: ""
val subject = str(map["subject".mp]) ?: ""
val body = str(map["body".mp]) ?: ""
val parents = LinkedList<InventoryVector>()
val files = LinkedList<Attachment>()
val mpParents = map[mp("parents")] as? MPArray<*>
val mpParents = map["parents".mp] as? MPArray<*>
for (parent in mpParents ?: emptyList<MPArray<MPBinary>>()) {
parents.add(InventoryVector.fromHash(
(parent as? MPBinary)?.value ?: continue
) ?: continue)
}
val mpFiles = map[mp("files")] as? MPArray<*>
val mpFiles = map["files".mp] as? MPArray<*>
for (item in mpFiles ?: emptyList<Any>()) {
if (item is MPMap<*, *>) {
val b = Attachment.Builder()
b.name(str(item[mp("name")])!!)
b.name(str(item["name".mp])!!)
b.data(
bin(item[mp("data")] ?: continue) ?: continue
bin(item["data".mp] ?: continue) ?: continue
)
b.type(str(item[mp("type")])!!)
val disposition = str(item[mp("disposition")])
b.type(str(item["type".mp])!!)
val disposition = str(item["disposition".mp])
if ("inline" == disposition) {
b.inline()
} else if ("attachment" == disposition) {

View File

@ -35,9 +35,9 @@ data class Vote constructor(val msgId: InventoryVector, val vote: String) : Exte
override fun pack(): MPMap<MPString, MPType<*>> {
val result = MPMap<MPString, MPType<*>>()
result.put(mp(""), mp(TYPE))
result.put(mp("msgId"), mp(*msgId.hash))
result.put(mp("vote"), mp(vote))
result.put("".mp, TYPE.mp)
result.put("msgId".mp, msgId.hash.mp)
result.put("vote".mp, vote.mp)
return result
}
@ -77,13 +77,14 @@ data class Vote constructor(val msgId: InventoryVector, val vote: String) : Exte
get() = TYPE
override fun unpack(map: MPMap<MPString, MPType<*>>): Vote {
val msgId = InventoryVector.fromHash((map[mp("msgId")] as? MPBinary)?.value) ?: throw IllegalArgumentException("data doesn't contain proper msgId")
val vote = str(map[mp("vote")]) ?: throw IllegalArgumentException("no vote given")
val msgId = InventoryVector.fromHash((map["msgId".mp] as? MPBinary)?.value) ?: throw IllegalArgumentException("data doesn't contain proper msgId")
val vote = str(map["vote".mp]) ?: throw IllegalArgumentException("no vote given")
return Vote(msgId, vote)
}
}
companion object {
@JvmField val TYPE = "vote"
@JvmField
val TYPE = "vote"
}
}

View File

@ -54,9 +54,8 @@ object ExtendedEncodingFactory {
fun unzip(zippedData: ByteArray): ExtendedEncoding? {
try {
InflaterInputStream(ByteArrayInputStream(zippedData)).use { unzipper ->
val reader = Reader.getInstance()
@Suppress("UNCHECKED_CAST")
val map = reader.read(unzipper) as MPMap<MPString, MPType<*>>
val map = Reader.read(unzipper) as MPMap<MPString, MPType<*>>
val messageType = map[KEY_MESSAGE_TYPE]
if (messageType == null) {
LOG.error("Missing message type")

View File

@ -31,4 +31,13 @@ interface NodeRegistry {
fun getKnownAddresses(limit: Int, vararg streams: Long): List<NetworkAddress>
fun offerAddresses(nodes: List<NetworkAddress>)
fun update(node: NetworkAddress)
fun remove(node: NetworkAddress)
/**
* Remove stale nodes
*/
fun cleanup()
}

View File

@ -29,7 +29,7 @@ object DebugUtils {
try {
val f = File(System.getProperty("user.home") + "/jabit.error/" + objectMessage.inventoryVector + ".inv")
f.createNewFile()
objectMessage.write(FileOutputStream(f))
objectMessage.writer().write(FileOutputStream(f))
} catch (e: IOException) {
LOG.debug(e.message, e)
}

View File

@ -26,22 +26,27 @@ import java.nio.ByteBuffer
* https://bitmessage.org/wiki/Protocol_specification#Common_structures
*/
object Encode {
@JvmStatic fun varIntList(values: LongArray, stream: OutputStream) {
@JvmStatic
fun varIntList(values: LongArray, stream: OutputStream) {
varInt(values.size, stream)
for (value in values) {
varInt(value, stream)
}
}
@JvmStatic fun varIntList(values: LongArray, buffer: ByteBuffer) {
@JvmStatic
fun varIntList(values: LongArray, buffer: ByteBuffer) {
varInt(values.size, buffer)
for (value in values) {
varInt(value, buffer)
}
}
@JvmStatic fun varInt(value: Int, buffer: ByteBuffer) = varInt(value.toLong(), buffer)
@JvmStatic fun varInt(value: Long, buffer: ByteBuffer) {
@JvmStatic
fun varInt(value: Int, buffer: ByteBuffer) = varInt(value.toLong(), buffer)
@JvmStatic
fun varInt(value: Long, buffer: ByteBuffer) {
if (value < 0) {
// This is due to the fact that Java doesn't really support unsigned values.
// Please be aware that this might be an error due to a smaller negative value being cast to long.
@ -63,16 +68,24 @@ object Encode {
}
}
@JvmStatic fun varInt(value: Int) = varInt(value.toLong())
@JvmStatic fun varInt(value: Long): ByteArray {
@JvmStatic
fun varInt(value: Int) = varInt(value.toLong())
@JvmStatic
fun varInt(value: Long): ByteArray {
val buffer = ByteBuffer.allocate(9)
varInt(value, buffer)
buffer.flip()
return Bytes.truncate(buffer.array(), buffer.limit())
}
@JvmStatic @JvmOverloads fun varInt(value: Int, stream: OutputStream, counter: AccessCounter? = null) = varInt(value.toLong(), stream, counter)
@JvmStatic @JvmOverloads fun varInt(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
@JvmStatic
@JvmOverloads
fun varInt(value: Int, stream: OutputStream, counter: AccessCounter? = null) = varInt(value.toLong(), stream, counter)
@JvmStatic
@JvmOverloads
fun varInt(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
val buffer = ByteBuffer.allocate(9)
varInt(value, buffer)
buffer.flip()
@ -80,46 +93,76 @@ object Encode {
AccessCounter.inc(counter, buffer.limit())
}
@JvmStatic @JvmOverloads fun int8(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int8(value.toInt(), stream, counter)
@JvmStatic @JvmOverloads fun int8(value: Int, stream: OutputStream, counter: AccessCounter? = null) {
@JvmStatic
@JvmOverloads
fun int8(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int8(value.toInt(), stream, counter)
@JvmStatic
@JvmOverloads
fun int8(value: Int, stream: OutputStream, counter: AccessCounter? = null) {
stream.write(value)
AccessCounter.inc(counter)
}
@JvmStatic @JvmOverloads fun int16(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int16(value.toShort(), stream, counter)
@JvmStatic @JvmOverloads fun int16(value: Int, stream: OutputStream, counter: AccessCounter? = null) = int16(value.toShort(), stream, counter)
@JvmStatic @JvmOverloads fun int16(value: Short, stream: OutputStream, counter: AccessCounter? = null) {
@JvmStatic
@JvmOverloads
fun int16(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int16(value.toShort(), stream, counter)
@JvmStatic
@JvmOverloads
fun int16(value: Int, stream: OutputStream, counter: AccessCounter? = null) = int16(value.toShort(), stream, counter)
@JvmStatic
@JvmOverloads
fun int16(value: Short, stream: OutputStream, counter: AccessCounter? = null) {
stream.write(ByteBuffer.allocate(2).putShort(value).array())
AccessCounter.inc(counter, 2)
}
@JvmStatic fun int16(value: Long, buffer: ByteBuffer) = int16(value.toShort(), buffer)
@JvmStatic fun int16(value: Int, buffer: ByteBuffer) = int16(value.toShort(), buffer)
@JvmStatic fun int16(value: Short, buffer: ByteBuffer) {
@JvmStatic
fun int16(value: Long, buffer: ByteBuffer) = int16(value.toShort(), buffer)
@JvmStatic
fun int16(value: Int, buffer: ByteBuffer) = int16(value.toShort(), buffer)
@JvmStatic
fun int16(value: Short, buffer: ByteBuffer) {
buffer.putShort(value)
}
@JvmStatic @JvmOverloads fun int32(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int32(value.toInt(), stream, counter)
@JvmStatic @JvmOverloads fun int32(value: Int, stream: OutputStream, counter: AccessCounter? = null) {
@JvmStatic
@JvmOverloads
fun int32(value: Long, stream: OutputStream, counter: AccessCounter? = null) = int32(value.toInt(), stream, counter)
@JvmStatic
@JvmOverloads
fun int32(value: Int, stream: OutputStream, counter: AccessCounter? = null) {
stream.write(ByteBuffer.allocate(4).putInt(value).array())
AccessCounter.inc(counter, 4)
}
@JvmStatic fun int32(value: Long, buffer: ByteBuffer) = int32(value.toInt(), buffer)
@JvmStatic fun int32(value: Int, buffer: ByteBuffer) {
@JvmStatic
fun int32(value: Long, buffer: ByteBuffer) = int32(value.toInt(), buffer)
@JvmStatic
fun int32(value: Int, buffer: ByteBuffer) {
buffer.putInt(value)
}
@JvmStatic @JvmOverloads fun int64(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
@JvmStatic
@JvmOverloads
fun int64(value: Long, stream: OutputStream, counter: AccessCounter? = null) {
stream.write(ByteBuffer.allocate(8).putLong(value).array())
AccessCounter.inc(counter, 8)
}
@JvmStatic fun int64(value: Long, buffer: ByteBuffer) {
@JvmStatic
fun int64(value: Long, buffer: ByteBuffer) {
buffer.putLong(value)
}
@JvmStatic fun varString(value: String, out: OutputStream) {
@JvmStatic
fun varString(value: String, out: OutputStream) {
val bytes = value.toByteArray(charset("utf-8"))
// Technically, it says the length in characters, but I think this one might be correct.
// It doesn't really matter, as only ASCII characters are being used.
@ -128,7 +171,8 @@ object Encode {
out.write(bytes)
}
@JvmStatic fun varString(value: String, buffer: ByteBuffer) {
@JvmStatic
fun varString(value: String, buffer: ByteBuffer) {
val bytes = value.toByteArray()
// Technically, it says the length in characters, but I think this one might be correct.
// It doesn't really matter, as only ASCII characters are being used.
@ -137,12 +181,14 @@ object Encode {
buffer.put(bytes)
}
@JvmStatic fun varBytes(data: ByteArray, out: OutputStream) {
@JvmStatic
fun varBytes(data: ByteArray, out: OutputStream) {
varInt(data.size.toLong(), out)
out.write(data)
}
@JvmStatic fun varBytes(data: ByteArray, buffer: ByteBuffer) {
@JvmStatic
fun varBytes(data: ByteArray, buffer: ByteBuffer) {
varInt(data.size.toLong(), buffer)
buffer.put(data)
}
@ -152,9 +198,10 @@ object Encode {
* @param streamable the object to be serialized
* @return an array of bytes representing the given streamable object.
*/
@JvmStatic fun bytes(streamable: Streamable): ByteArray {
@JvmStatic
fun bytes(streamable: Streamable): ByteArray {
val stream = ByteArrayOutputStream()
streamable.write(stream)
streamable.writer().write(stream)
return stream.toByteArray()
}
@ -163,9 +210,10 @@ object Encode {
* @param padding the result will be padded such that its length is a multiple of *padding*
* @return the bytes of the given [Streamable] object, 0-padded such that the final length is x*padding.
*/
@JvmStatic fun bytes(streamable: Streamable, padding: Int): ByteArray {
@JvmStatic
fun bytes(streamable: Streamable, padding: Int): ByteArray {
val stream = ByteArrayOutputStream()
streamable.write(stream)
streamable.writer().write(stream)
val offset = padding - stream.size() % padding
val length = stream.size() + offset
val result = ByteArray(length)

View File

@ -86,7 +86,7 @@ class SerializationTest : TestBase() {
.signature(ByteArray(0))
.build()
val out = ByteArrayOutputStream()
expected.write(out)
expected.writer().write(out)
val `in` = ByteArrayInputStream(out.toByteArray())
val actual = Plaintext.read(MSG, `in`)
@ -111,7 +111,7 @@ class SerializationTest : TestBase() {
.signature(ByteArray(0))
.build()
val out = ByteArrayOutputStream()
expected.write(out)
expected.writer().write(out)
val `in` = ByteArrayInputStream(out.toByteArray())
val actual = Plaintext.read(MSG, `in`)
@ -136,7 +136,7 @@ class SerializationTest : TestBase() {
assertNotNull(ackMessage1)
val out = ByteArrayOutputStream()
expected.write(out)
expected.writer().write(out)
val `in` = ByteArrayInputStream(out.toByteArray())
val actual = Plaintext.read(MSG, `in`)
@ -159,7 +159,7 @@ class SerializationTest : TestBase() {
val inv = Inv(ivs)
val before = NetworkMessage(inv)
val out = ByteArrayOutputStream()
before.write(out)
before.writer().write(out)
val after = Factory.getNetworkMessage(3, ByteArrayInputStream(out.toByteArray()))
assertNotNull(after)
@ -173,7 +173,7 @@ class SerializationTest : TestBase() {
val objectMessage = Factory.getObjectMessage(version, `in`, data.size)
val out = ByteArrayOutputStream()
assertNotNull(objectMessage)
objectMessage!!.write(out)
objectMessage!!.writer().write(out)
assertArrayEquals(data, out.toByteArray())
assertEquals(expectedPayloadType.canonicalName, objectMessage.payload.javaClass.canonicalName)
}

View File

@ -24,6 +24,7 @@ import ch.dissem.bitmessage.ports.NodeRegistry;
import ch.dissem.bitmessage.repository.*;
import ch.dissem.bitmessage.wif.WifExporter;
import ch.dissem.bitmessage.wif.WifImporter;
import org.jetbrains.annotations.NotNull;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
@ -68,12 +69,28 @@ public class Main {
if (options.localPort != null) {
ctxBuilder.nodeRegistry(new NodeRegistry() {
@Override
public void clear() {
public void cleanup() {
// NO OP
}
@Override
public List<NetworkAddress> getKnownAddresses(int limit, long... streams) {
public void remove(@NotNull NetworkAddress node) {
// NO OP
}
@Override
public void update(@NotNull NetworkAddress node) {
// NO OP
}
@Override
public void clear() {
// NO OP
}
@NotNull
@Override
public List<NetworkAddress> getKnownAddresses(int limit, @NotNull long... streams) {
return Arrays.stream(streams)
.mapToObj(s -> new NetworkAddress.Builder()
.ipv4(127, 0, 0, 1)
@ -83,7 +100,7 @@ public class Main {
}
@Override
public void offerAddresses(List<NetworkAddress> nodes) {
public void offerAddresses(@NotNull List<NetworkAddress> nodes) {
LOG.info("Local node registry ignored offered addresses: " + nodes);
}
});

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.ports.NodeRegistry;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedList;
import java.util.List;
@ -44,13 +45,29 @@ class TestNodeRegistry implements NodeRegistry {
// NO OP
}
@NotNull
@Override
public List<NetworkAddress> getKnownAddresses(int limit, long... streams) {
public List<NetworkAddress> getKnownAddresses(int limit, @NotNull long... streams) {
return nodes;
}
@Override
public void offerAddresses(List<NetworkAddress> nodes) {
public void offerAddresses(@NotNull List<NetworkAddress> nodes) {
// Ignore
}
@Override
public void update(@NotNull NetworkAddress node) {
// Ignore
}
@Override
public void remove(@NotNull NetworkAddress node) {
// Ignore
}
@Override
public void cleanup() {
// Ignore
}
}

View File

@ -15,9 +15,9 @@ dependencies {
compile 'org.slf4j:slf4j-api'
compile 'com.beust:klaxon'
testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:hamcrest-library:1.3'
testCompile 'com.nhaarman:mockito-kotlin:1.5.0'
testCompile 'junit:junit'
testCompile 'org.hamcrest:hamcrest-library'
testCompile 'com.nhaarman:mockito-kotlin'
testCompile project(path: ':core', configuration: 'testArtifacts')
testCompile project(':cryptography-bc')
}

View File

@ -39,7 +39,7 @@ object ContactExport {
"subscribed" to it.isSubscribed,
"pubkey" to it.pubkey?.let {
val out = ByteArrayOutputStream()
it.writeUnencrypted(out)
it.writer().writeUnencrypted(out)
Base64.encodeToString(out.toByteArray())
},
"privateKey" to if (includePrivateKey) {

View File

@ -19,6 +19,7 @@ package ch.dissem.bitmessage.extensions
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.CustomMessage
import ch.dissem.bitmessage.entity.Streamable
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.entity.payload.CryptoBox
import ch.dissem.bitmessage.entity.payload.Pubkey
import ch.dissem.bitmessage.exception.DecryptionFailedException
@ -73,7 +74,7 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
Encode.varInt(privateKey.pubkey.extraBytes, out)
}
data?.write(out) ?: throw IllegalStateException("no unencrypted data available")
data?.writer()?.write(out) ?: throw IllegalStateException("no unencrypted data available")
Encode.varBytes(cryptography().getSignature(out.toByteArray(), privateKey), out)
container = CryptoBox(out.toByteArray(), publicKey)
}
@ -109,9 +110,17 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
return data!!
}
override fun write(out: OutputStream) {
Encode.varString(COMMAND, out)
container?.write(out) ?: throw IllegalStateException("not encrypted yet")
override fun writer(): StreamableWriter = Writer(this)
private class Writer(
private val item: CryptoCustomMessage<*>
) : CustomMessage.Writer(item) {
override fun write(out: OutputStream) {
Encode.varString(COMMAND, out)
item.container?.writer()?.write(out) ?: throw IllegalStateException("not encrypted yet")
}
}
interface Reader<out T> {
@ -136,9 +145,11 @@ class CryptoCustomMessage<T : Streamable> : CustomMessage {
}
companion object {
@JvmField val COMMAND = "ENCRYPTED"
@JvmField
val COMMAND = "ENCRYPTED"
@JvmStatic fun <T : Streamable> read(data: CustomMessage, dataReader: Reader<T>): CryptoCustomMessage<T> {
@JvmStatic
fun <T : Streamable> read(data: CustomMessage, dataReader: Reader<T>): CryptoCustomMessage<T> {
val cryptoBox = CryptoBox.read(ByteArrayInputStream(data.getData()), data.getData().size)
return CryptoCustomMessage(cryptoBox, dataReader)
}

View File

@ -18,6 +18,7 @@ package ch.dissem.bitmessage.extensions.pow
import ch.dissem.bitmessage.entity.BitmessageAddress
import ch.dissem.bitmessage.entity.Streamable
import ch.dissem.bitmessage.entity.StreamableWriter
import ch.dissem.bitmessage.extensions.CryptoCustomMessage
import ch.dissem.bitmessage.utils.Decode.bytes
import ch.dissem.bitmessage.utils.Decode.varBytes
@ -33,16 +34,24 @@ import java.util.*
*/
data class ProofOfWorkRequest @JvmOverloads constructor(val sender: BitmessageAddress, val initialHash: ByteArray, val request: ProofOfWorkRequest.Request, val data: ByteArray = ByteArray(0)) : Streamable {
override fun write(out: OutputStream) {
out.write(initialHash)
Encode.varString(request.name, out)
Encode.varBytes(data, out)
}
override fun writer(): StreamableWriter = Writer(this)
private class Writer(
private val item: ProofOfWorkRequest
) : StreamableWriter {
override fun write(out: OutputStream) {
out.write(item.initialHash)
Encode.varString(item.request.name, out)
Encode.varBytes(item.data, out)
}
override fun write(buffer: ByteBuffer) {
buffer.put(item.initialHash)
Encode.varString(item.request.name, buffer)
Encode.varBytes(item.data, buffer)
}
override fun write(buffer: ByteBuffer) {
buffer.put(initialHash)
Encode.varString(request.name, buffer)
Encode.varBytes(data, buffer)
}
class Reader(private val identity: BitmessageAddress) : CryptoCustomMessage.Reader<ProofOfWorkRequest> {

View File

@ -43,7 +43,7 @@ class CryptoCustomMessageTest : TestBase() {
messageBefore.signAndEncrypt(sendingIdentity, cryptography().createPublicKey(sendingIdentity.publicDecryptionKey))
val out = ByteArrayOutputStream()
messageBefore.write(out)
messageBefore.writer().write(out)
val `in` = ByteArrayInputStream(out.toByteArray())
val customMessage = CustomMessage.read(`in`, out.size())
@ -71,7 +71,7 @@ class CryptoCustomMessageTest : TestBase() {
val out = ByteArrayOutputStream()
messageBefore.write(out)
messageBefore.writer().write(out)
val `in` = ByteArrayInputStream(out.toByteArray())
val customMessage = CustomMessage.read(`in`, out.size())

Binary file not shown.

View File

@ -1,6 +1,6 @@
#Mon Jul 17 06:32:41 CEST 2017
#Tue Nov 07 17:21:36 CET 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-all.zip

6
gradlew vendored
View File

@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
warn () {
echo "$*"
}
die ( ) {
die () {
echo
echo "$*"
echo
@ -155,7 +155,7 @@ if $cygwin ; then
fi
# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}

View File

@ -61,7 +61,7 @@ class ConnectionIO(
if (!headerOut.hasRemaining() && !sendingQueue.isEmpty()) {
headerOut.clear()
val payload = sendingQueue.poll()
payloadOut = NetworkMessage(payload).writeHeaderAndGetPayloadBuffer(headerOut)
payloadOut = NetworkMessage(payload).writer().writeHeaderAndGetPayloadBuffer(headerOut)
headerOut.flip()
lastUpdate = System.currentTimeMillis()
}

View File

@ -90,7 +90,7 @@ class NioNetworkHandler : NetworkHandler, InternalContext.ContextHolder {
SocketChannel.open(InetSocketAddress(server, port)).use { channel ->
channel.configureBlocking(true)
val headerBuffer = ByteBuffer.allocate(HEADER_SIZE)
val payloadBuffer = NetworkMessage(request).writeHeaderAndGetPayloadBuffer(headerBuffer)
val payloadBuffer = NetworkMessage(request).writer().writeHeaderAndGetPayloadBuffer(headerBuffer)
headerBuffer.flip()
while (headerBuffer.hasRemaining()) {
channel.write(headerBuffer)

View File

@ -38,4 +38,16 @@ internal class TestNodeRegistry(vararg nodes: NetworkAddress) : NodeRegistry {
override fun offerAddresses(nodes: List<NetworkAddress>) {
// Ignore
}
override fun update(node: NetworkAddress) {
// Ignore
}
override fun remove(node: NetworkAddress) {
// Ignore
}
override fun cleanup() {
// Ignore
}
}

View File

@ -175,7 +175,7 @@ class JdbcAddressRepository(config: JdbcConfig) : JdbcHelper(config), AddressRep
private fun writePubkey(ps: PreparedStatement, parameterIndex: Int, data: Pubkey?) {
if (data != null) {
val out = ByteArrayOutputStream()
data.writeUnencrypted(out)
data.writer().writeUnencrypted(out)
ps.setBytes(parameterIndex, out.toByteArray())
} else {
ps.setBytes(parameterIndex, null)

View File

@ -30,7 +30,7 @@ abstract class JdbcHelper protected constructor(@JvmField protected val config:
ps.setBytes(parameterIndex, null)
} else {
val os = ByteArrayOutputStream()
data.write(os)
data.writer().write(os)
ps.setBytes(parameterIndex, os.toByteArray())
}
}

View File

@ -19,9 +19,8 @@ package ch.dissem.bitmessage.repository
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress
import ch.dissem.bitmessage.ports.NodeRegistry
import ch.dissem.bitmessage.ports.NodeRegistryHelper.loadStableNodes
import ch.dissem.bitmessage.utils.*
import ch.dissem.bitmessage.utils.Collections
import ch.dissem.bitmessage.utils.SqlStrings
import ch.dissem.bitmessage.utils.Strings
import ch.dissem.bitmessage.utils.UnixTime.DAY
import ch.dissem.bitmessage.utils.UnixTime.MINUTE
import ch.dissem.bitmessage.utils.UnixTime.now
@ -155,7 +154,18 @@ class JdbcNodeRegistry(config: JdbcConfig) : JdbcHelper(config), NodeRegistry {
ps.setBytes(2, node.IPv6)
ps.setInt(3, node.port)
ps.setLong(4, node.services)
ps.setLong(5, node.time)
ps.setLong(5,
if (node.time > UnixTime.now) {
// This might be an attack, let's not use those nodes with priority
UnixTime.now - 7 * UnixTime.DAY
} else if (node.time == 0L) {
// Those just don't have a time set
// let's give them slightly higher priority than the possible attack ones
UnixTime.now - 6 * UnixTime.DAY
} else {
node.time
}
)
ps.executeUpdate()
}
}
@ -164,13 +174,20 @@ class JdbcNodeRegistry(config: JdbcConfig) : JdbcHelper(config), NodeRegistry {
}
}
private fun update(node: NetworkAddress) {
override fun update(node: NetworkAddress) {
try {
val time = if (node.time > UnixTime.now) {
// This might be an attack, let's not use those nodes with priority
UnixTime.now - 7 * UnixTime.DAY
} else {
node.time
}
config.getConnection().use { connection ->
connection.prepareStatement(
"UPDATE Node SET services=?, time=? WHERE stream=? AND address=? AND port=?").use { ps ->
ps.setLong(1, node.services)
ps.setLong(2, node.time)
ps.setLong(2, max(node.time, time))
ps.setLong(3, node.stream)
ps.setBytes(4, node.IPv6)
ps.setInt(5, node.port)
@ -182,6 +199,36 @@ class JdbcNodeRegistry(config: JdbcConfig) : JdbcHelper(config), NodeRegistry {
}
}
override fun remove(node: NetworkAddress) {
try {
config.getConnection().use { connection ->
connection.prepareStatement(
"DELETE FROM Node WHERE stream=? AND address=? AND port=?").use { ps ->
ps.setLong(1, node.stream)
ps.setBytes(2, node.IPv6)
ps.setInt(3, node.port)
ps.executeUpdate()
}
}
} catch (e: SQLException) {
LOG.error(e.message, e)
}
}
override fun cleanup() {
try {
config.getConnection().use { connection ->
connection.prepareStatement(
"DELETE FROM Node WHERE time<?").use { ps ->
ps.setLong(1, UnixTime.now - 8 * DAY)
ps.executeUpdate()
}
}
} catch (e: SQLException) {
LOG.error(e.message, e)
}
}
companion object {
private val LOG = LoggerFactory.getLogger(JdbcNodeRegistry::class.java)
}