add extended signature in queries to prevent replay attacks

This commit is contained in:
Zlatin Balevsky
2019-11-09 16:39:16 +00:00
parent afaadf65a4
commit f4a2864942
6 changed files with 62 additions and 6 deletions

View File

@@ -153,6 +153,10 @@ abstract class Connection implements Closeable {
query.originator = e.originator.toBase64()
if (e.sig != null)
query.sig = Base64.encode(e.sig)
if (e.queryTime > 0)
query.queryTime = e.queryTime
if (e.sig2 != null)
query.sig2 = Base64.encode(e.sig2)
messages.put(query)
}
@@ -250,6 +254,32 @@ abstract class Connection implements Closeable {
} else
log.info("no signature in query")
// TODO: make this mandatory at some point
byte[] sig2 = null
long queryTime = 0
if (search.sig2 != null) {
if (search.queryTime == null) {
log.info("extended signature but no timestamp")
return
}
sig2 = Base64.decode(search.sig2)
queryTime = search.queryTime
byte [] payload = (search.uuid + String.valueOf(queryTime)).getBytes(StandardCharsets.US_ASCII)
def spk = originator.destination.getSigningPublicKey()
def signature = new Signature(Constants.SIG_TYPE, sig2)
if (!DSAEngine.getInstance().verifySignature(signature, payload, spk)) {
log.info("extended signature didn't match uuid and timestamp")
return
} else {
log.info("extended query signature verified")
if (queryTime < System.currentTimeMillis() - Constants.MAX_QUERY_AGE) {
log.info("query too old")
return
}
}
} else
log.info("no extended signature in query")
SearchEvent searchEvent = new SearchEvent(searchTerms : search.keywords,
searchHash : infohash,
uuid : uuid,
@@ -262,7 +292,9 @@ abstract class Connection implements Closeable {
originator : originator,
receivedOn : endpoint.destination,
firstHop : search.firstHop,
sig : sig )
sig : sig,
queryTime : queryTime,
sig2 : sig2 )
eventBus.publish(event)
}

View File

@@ -13,6 +13,8 @@ class QueryEvent extends Event {
Persona originator
Destination receivedOn
byte[] sig
long queryTime
byte[] sig2
String toString() {
"searchEvent: $searchEvent firstHop:$firstHop, replyTo:${replyTo.toBase32()}" +

View File

@@ -13,6 +13,7 @@ import com.muwire.core.files.FileSharedEvent
import com.muwire.core.search.QueryEvent
import com.muwire.core.search.SearchEvent
import com.muwire.core.search.UIResultBatchEvent
import com.muwire.core.util.DataUtil
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
@@ -176,9 +177,12 @@ class UpdateClient {
signer = payload.signer
log.info("starting search for new version hash $payload.infoHash")
Signature sig = DSAEngine.getInstance().sign(updateInfoHash.getRoot(), spk)
def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : UUID.randomUUID(), oobInfohash : true, persona : me)
UUID uuid = UUID.randomUUID()
long timestamp = System.currentTimeMillis()
byte [] sig2 = DataUtil.signUUID(uuid, timestamp, spk)
def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : uuid, oobInfohash : true, persona : me)
def queryEvent = new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo : me.destination,
receivedOn : me.destination, originator : me, sig : sig.data)
receivedOn : me.destination, originator : me, sig : sig.data, queryTime : timestamp, sig2 : sig2)
eventBus.publish(queryEvent)
}
}

View File

@@ -13,4 +13,6 @@ public class Constants {
public static final int MAX_RESULTS = 0x1 << 16;
public static final int MAX_COMMENT_LENGTH = 0x1 << 15;
public static final long MAX_QUERY_AGE = 5 * 60 * 1000L;
}

View File

@@ -15,11 +15,15 @@ import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import com.muwire.core.Constants;
import net.i2p.crypto.DSAEngine;
import net.i2p.data.Base64;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
import net.i2p.util.ConcurrentHashSet;
public class DataUtil {
@@ -203,4 +207,10 @@ public class DataUtil {
.collect(Collectors.joining(","));
props.setProperty(property, encoded);
}
public static byte[] signUUID(UUID uuid, long timestamp, SigningPrivateKey spk) {
byte [] payload = (uuid.toString() + String.valueOf(timestamp)).getBytes(StandardCharsets.US_ASCII);
Signature sig = DSAEngine.getInstance().sign(payload, spk);
return sig.getData();
}
}

View File

@@ -7,9 +7,11 @@ import griffon.core.mvc.MVCGroup
import griffon.core.mvc.MVCGroupConfiguration
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import groovy.json.StringEscapeUtils
import net.i2p.crypto.DSAEngine
import net.i2p.data.Base64
import net.i2p.data.Signature
import net.i2p.data.SigningPrivateKey
import java.awt.Desktop
import java.awt.event.ActionEvent
@@ -43,6 +45,7 @@ import com.muwire.core.trust.TrustLevel
import com.muwire.core.trust.TrustSubscriptionEvent
import com.muwire.core.upload.HashListUploader
import com.muwire.core.upload.Uploader
import com.muwire.core.util.DataUtil
@ArtifactProviderFor(GriffonController)
class MainFrameController {
@@ -121,9 +124,10 @@ class MainFrameController {
Signature sig = DSAEngine.getInstance().sign(payload, core.spk)
long timestamp = System.currentTimeMillis()
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : firstHop,
replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me, sig : sig.data))
originator : core.me, sig : sig.data, queryTime : timestamp, sig2 : DataUtil.signUUID(uuid, timestamp, core.spk)))
}
@@ -140,12 +144,14 @@ class MainFrameController {
byte [] infoHashBytes = Base64.decode(infoHash)
Signature sig = DSAEngine.getInstance().sign(infoHashBytes, core.spk)
long timestamp = System.currentTimeMillis()
byte [] sig2 = DataUtil.signUUID(uuid, timestamp, core.spk)
def searchEvent = new SearchEvent(searchHash : Base64.decode(infoHash), uuid:uuid,
oobInfohash: true, persona : core.me)
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me, sig : sig.data))
originator : core.me, sig : sig.data, queryTime : timestamp, sig2 : sig2))
}
private int selectedDownload() {