Add support for comments in certificates, bump certificate version
This commit is contained in:
@@ -14,7 +14,7 @@ import net.i2p.data.SigningPublicKey
|
||||
class Certificate {
|
||||
private final byte version
|
||||
private final InfoHash infoHash
|
||||
private final Name name
|
||||
private final Name name, comment
|
||||
private final long timestamp
|
||||
private final Persona issuer
|
||||
private final byte[] sig
|
||||
@@ -23,7 +23,7 @@ class Certificate {
|
||||
|
||||
Certificate(InputStream is) {
|
||||
version = (byte) (is.read() & 0xFF)
|
||||
if (version != Constants.FILE_CERT_VERSION)
|
||||
if (version > Constants.FILE_CERT_VERSION)
|
||||
throw new IOException("Unknown version $version")
|
||||
|
||||
DataInputStream dis = new DataInputStream(is)
|
||||
@@ -35,19 +35,29 @@ class Certificate {
|
||||
|
||||
name = new Name(dis)
|
||||
|
||||
issuer = new Persona(is)
|
||||
issuer = new Persona(dis)
|
||||
if (version == 2) {
|
||||
byte present = (byte)(dis.read() & 0xFF)
|
||||
if (present != 0) {
|
||||
comment = new Name(dis)
|
||||
}
|
||||
}
|
||||
|
||||
sig = new byte[Constants.SIG_TYPE.getSigLen()]
|
||||
dis.readFully(sig)
|
||||
|
||||
if (!verify(version, infoHash, name, timestamp, issuer, sig))
|
||||
if (!verify(version, infoHash, name, timestamp, issuer, comment, sig))
|
||||
throw new InvalidSignatureException("certificate for $name.name from ${issuer.getHumanReadableName()} didn't verify")
|
||||
}
|
||||
|
||||
Certificate(InfoHash infoHash, String name, long timestamp, Persona issuer, SigningPrivateKey spk) {
|
||||
Certificate(InfoHash infoHash, String name, long timestamp, Persona issuer, String comment, SigningPrivateKey spk) {
|
||||
this.version = Constants.FILE_CERT_VERSION
|
||||
this.infoHash = infoHash
|
||||
this.name = new Name(name)
|
||||
if (comment != null)
|
||||
this.comment = new Name(comment)
|
||||
else
|
||||
this.comment = null
|
||||
this.timestamp = timestamp
|
||||
this.issuer = issuer
|
||||
|
||||
@@ -59,6 +69,12 @@ class Certificate {
|
||||
daos.write(infoHash.getRoot())
|
||||
this.name.write(daos)
|
||||
issuer.write(daos)
|
||||
if (this.comment == null) {
|
||||
daos.write((byte) 0)
|
||||
} else {
|
||||
daos.write((byte) 1)
|
||||
this.comment.write(daos)
|
||||
}
|
||||
daos.close()
|
||||
|
||||
byte[] payload = baos.toByteArray()
|
||||
@@ -66,7 +82,7 @@ class Certificate {
|
||||
this.sig = signature.getData()
|
||||
}
|
||||
|
||||
private static boolean verify(byte version, InfoHash infoHash, Name name, long timestamp, Persona issuer, byte[] sig) {
|
||||
private static boolean verify(byte version, InfoHash infoHash, Name name, long timestamp, Persona issuer, Name comment, byte[] sig) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()
|
||||
DataOutputStream daos = new DataOutputStream(baos)
|
||||
daos.write(version)
|
||||
@@ -74,6 +90,14 @@ class Certificate {
|
||||
daos.write(infoHash.getRoot())
|
||||
name.write(daos)
|
||||
issuer.write(daos)
|
||||
if (version == 2) {
|
||||
if (comment == null) {
|
||||
daos.write((byte)0)
|
||||
} else {
|
||||
daos.write((byte)1)
|
||||
comment.write(daos)
|
||||
}
|
||||
}
|
||||
daos.close()
|
||||
|
||||
byte [] payload = baos.toByteArray()
|
||||
@@ -91,6 +115,12 @@ class Certificate {
|
||||
daos.write(infoHash.getRoot())
|
||||
name.write(daos)
|
||||
issuer.write(daos)
|
||||
if (comment == null)
|
||||
daos.write((byte) 0)
|
||||
else {
|
||||
daos.write((byte) 1)
|
||||
comment.write(daos)
|
||||
}
|
||||
daos.write(sig)
|
||||
daos.close()
|
||||
|
||||
@@ -114,6 +144,7 @@ class Certificate {
|
||||
infoHash == other.infoHash &&
|
||||
timestamp == other.timestamp &&
|
||||
name == other.name &&
|
||||
issuer == other.issuer
|
||||
issuer == other.issuer &&
|
||||
comment == other.comment
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import com.muwire.core.InfoHash
|
||||
import com.muwire.core.InvalidSignatureException
|
||||
import com.muwire.core.Name
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Base64
|
||||
@@ -72,7 +73,12 @@ class CertificateManager {
|
||||
InfoHash infoHash = e.sharedFile.getInfoHash()
|
||||
String name = e.sharedFile.getFile().getName()
|
||||
long timestamp = System.currentTimeMillis()
|
||||
Certificate cert = new Certificate(infoHash, name, timestamp, me, spk)
|
||||
|
||||
String comment = null
|
||||
if (e.sharedFile.getComment() != null)
|
||||
comment = DataUtil.readi18nString(Base64.decode(e.sharedFile.getComment()))
|
||||
|
||||
Certificate cert = new Certificate(infoHash, name, timestamp, me, comment, spk)
|
||||
|
||||
|
||||
if (addToMaps(cert)) {
|
||||
|
@@ -4,7 +4,7 @@ import net.i2p.crypto.SigType;
|
||||
|
||||
public class Constants {
|
||||
public static final byte PERSONA_VERSION = (byte)1;
|
||||
public static final byte FILE_CERT_VERSION = (byte)1;
|
||||
public static final byte FILE_CERT_VERSION = (byte)2;
|
||||
public static final SigType SIG_TYPE = SigType.EdDSA_SHA512_Ed25519;
|
||||
|
||||
public static final int MAX_HEADER_SIZE = 0x1 << 14;
|
||||
|
@@ -6,6 +6,23 @@ import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
import com.muwire.core.filecert.Certificate
|
||||
|
||||
@ArtifactProviderFor(GriffonController)
|
||||
class CertificateControlController {
|
||||
@MVCMember @Nonnull
|
||||
CertificateControlModel model
|
||||
@MVCMember @Nonnull
|
||||
CertificateControlView view
|
||||
|
||||
@ControllerAction
|
||||
void showComment() {
|
||||
Certificate cert = view.getSelectedSertificate()
|
||||
if (cert == null || cert.comment == null)
|
||||
return
|
||||
|
||||
def params = [:]
|
||||
params['text'] = cert.comment.name
|
||||
mvcGroup.createMVCGroup("show-comment", params)
|
||||
}
|
||||
}
|
@@ -63,6 +63,19 @@ class FetchCertificatesController {
|
||||
JOptionPane.showMessageDialog(null, "Certificates imported.")
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void showComment() {
|
||||
def selectedCerts = view.selectedCertificates()
|
||||
if (selectedCerts == null || selectedCerts.size() != 1)
|
||||
return
|
||||
|
||||
String comment = selectedCerts[0].comment.name
|
||||
def params = [:]
|
||||
params['text'] = comment
|
||||
params['name'] = "Certificate Comment"
|
||||
mvcGroup.createMVCGroup("show-comment", params)
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void dismiss() {
|
||||
view.dialog.setVisible(false)
|
||||
|
@@ -117,7 +117,8 @@ class SearchTabController {
|
||||
|
||||
String groupId = Base64.encode(event.infohash.getRoot())
|
||||
Map<String,Object> params = new HashMap<>()
|
||||
params['result'] = event
|
||||
params['text'] = event.comment
|
||||
params['name'] = event.name
|
||||
|
||||
mvcGroup.createMVCGroup("show-comment", groupId, params)
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@ class CertificateControlModel {
|
||||
|
||||
Core core
|
||||
|
||||
@Observable boolean showCommentActionEnabled
|
||||
|
||||
void mvcGroupInit(Map<String,String> args) {
|
||||
users.addAll(core.certificateManager.byIssuer.keySet())
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ class FetchCertificatesModel {
|
||||
@Observable int certificateCount
|
||||
|
||||
@Observable boolean importActionEnabled
|
||||
@Observable boolean showCommentActionEnabled
|
||||
|
||||
def certificates = []
|
||||
}
|
@@ -8,5 +8,6 @@ import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
@ArtifactProviderFor(GriffonModel)
|
||||
class ShowCommentModel {
|
||||
UIResultEvent result
|
||||
String name
|
||||
String text
|
||||
}
|
@@ -6,6 +6,8 @@ import griffon.metadata.ArtifactProviderFor
|
||||
import net.i2p.data.Base64
|
||||
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JMenuItem
|
||||
import javax.swing.JPopupMenu
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
@@ -13,6 +15,8 @@ import com.muwire.core.Persona
|
||||
import com.muwire.core.filecert.Certificate
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.event.MouseAdapter
|
||||
import java.awt.event.MouseEvent
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
|
||||
@@ -24,6 +28,8 @@ class CertificateControlView {
|
||||
FactoryBuilderSupport builder
|
||||
@MVCMember @Nonnull
|
||||
CertificateControlModel model
|
||||
@MVCMember @Nonnull
|
||||
CertificateControlController controller
|
||||
|
||||
def mainFrame
|
||||
def dialog
|
||||
@@ -31,6 +37,7 @@ class CertificateControlView {
|
||||
def usersTable
|
||||
def certsTable
|
||||
def lastUsersSortEvent
|
||||
def lastCertsSortEvent
|
||||
|
||||
void initUI() {
|
||||
mainFrame = application.windowManager.findWindow("main-frame")
|
||||
@@ -57,6 +64,7 @@ class CertificateControlView {
|
||||
tableModel(list : model.certificates) {
|
||||
closureColumn(header : "File Name", type : String, read : {it.name.name})
|
||||
closureColumn(header : "Hash", type : String, read : {Base64.encode(it.infoHash.getRoot())})
|
||||
closureColumn(header : "Comment", preferredWidth : 20, type : Boolean, read : {it.comment != null})
|
||||
closureColumn(header : "Timestamp", type : String, read : {
|
||||
def date = new Date(it.timestamp)
|
||||
date.toString()
|
||||
@@ -65,6 +73,9 @@ class CertificateControlView {
|
||||
}
|
||||
}
|
||||
}
|
||||
panel (constraints : BorderLayout.SOUTH) {
|
||||
button(text : "Show Comment", enabled : bind {model.showCommentActionEnabled}, showCommentAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +95,24 @@ class CertificateControlView {
|
||||
certsTable.model.fireTableDataChanged()
|
||||
})
|
||||
|
||||
certsTable.rowSorter.addRowSorterListener({evt -> lastCertsSortEvent = evt})
|
||||
selectionModel = certsTable.getSelectionModel()
|
||||
selectionModel.addListSelectionListener({
|
||||
Certificate c = getSelectedSertificate()
|
||||
model.showCommentActionEnabled = c != null && c.comment != null
|
||||
})
|
||||
|
||||
certsTable.addMouseListener(new MouseAdapter() {
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (e.isPopupTrigger())
|
||||
showMenu(e)
|
||||
}
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (e.isPopupTrigger())
|
||||
showMenu(e)
|
||||
}
|
||||
})
|
||||
|
||||
dialog.getContentPane().add(panel)
|
||||
dialog.pack()
|
||||
dialog.setLocationRelativeTo(mainFrame)
|
||||
@@ -105,4 +134,23 @@ class CertificateControlView {
|
||||
model.users[selectedRow]
|
||||
}
|
||||
|
||||
Certificate getSelectedSertificate() {
|
||||
int [] selectedRows = certsTable.getSelectedRows()
|
||||
if (selectedRows.length != 1)
|
||||
return null
|
||||
if (lastCertsSortEvent != null)
|
||||
selectedRows[0] = certsTable.rowSorter.convertRowIndexToModel(selectedRows[0])
|
||||
model.certificates[selectedRows[0]]
|
||||
}
|
||||
|
||||
private void showMenu(MouseEvent e) {
|
||||
if (!model.showCommentActionEnabled)
|
||||
return
|
||||
JPopupMenu menu = new JPopupMenu()
|
||||
JMenuItem showComment = new JMenuItem("Show Comment")
|
||||
showComment.addActionListener({controller.showComment()})
|
||||
menu.add(showComment)
|
||||
menu.show(e.getComponent(), e.getX(), e.getY())
|
||||
}
|
||||
|
||||
}
|
@@ -57,11 +57,13 @@ class FetchCertificatesView {
|
||||
def date = new Date(it.timestamp)
|
||||
date.toString()
|
||||
})
|
||||
closureColumn(header : "Comments", preferredWidth: 20, type : Boolean, read :{it.comment != null})
|
||||
}
|
||||
}
|
||||
}
|
||||
panel(constraints : BorderLayout.SOUTH) {
|
||||
button(text : "Import", enabled : bind {model.importActionEnabled}, importCertificatesAction)
|
||||
button(text : "Show Comment", enabled : bind {model.showCommentActionEnabled}, showCommentAction)
|
||||
button(text : "Dismiss", dismissAction)
|
||||
}
|
||||
}
|
||||
@@ -74,6 +76,14 @@ class FetchCertificatesView {
|
||||
selectionModel.addListSelectionListener({
|
||||
int[] rows = certsTable.getSelectedRows()
|
||||
model.importActionEnabled = rows.length > 0
|
||||
|
||||
if (rows.length == 1) {
|
||||
if (lastSortEvent != null)
|
||||
rows[0] = certsTable.rowSorter.convertRowIndexToModel(rows[0])
|
||||
model.showCommentActionEnabled = model.certificates[rows[0]].comment != null
|
||||
} else
|
||||
model.showCommentActionEnabled = false
|
||||
|
||||
})
|
||||
|
||||
certsTable.addMouseListener(new MouseAdapter() {
|
||||
@@ -94,7 +104,12 @@ class FetchCertificatesView {
|
||||
JMenuItem importItem = new JMenuItem("Import")
|
||||
importItem.addActionListener({controller.importCertificates()})
|
||||
menu.add(importItem)
|
||||
menu.showing(e.getComponent(), e.getX(), e.getY())
|
||||
if (model.showCommentActionEnabled) {
|
||||
JMenuItem showComment = new JMenuItem("Show Comment")
|
||||
showComment.addActionListener({controller.showComment()})
|
||||
menu.add(showComment)
|
||||
}
|
||||
menu.show(e.getComponent(), e.getX(), e.getY())
|
||||
}
|
||||
|
||||
def selectedCertificates() {
|
||||
|
@@ -29,7 +29,7 @@ class ShowCommentView {
|
||||
|
||||
void initUI() {
|
||||
mainFrame = application.windowManager.findWindow("main-frame")
|
||||
dialog = new JDialog(mainFrame, model.result.name, true)
|
||||
dialog = new JDialog(mainFrame, model.name, true)
|
||||
dialog.setResizable(true)
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class ShowCommentView {
|
||||
borderLayout()
|
||||
panel (constraints : BorderLayout.CENTER) {
|
||||
scrollPane {
|
||||
textArea(text : model.result.comment, rows : 20, columns : 100, editable : false, lineWrap : true, wrapStyleWord : true)
|
||||
textArea(text : model.text, rows : 20, columns : 100, editable : false, lineWrap : true, wrapStyleWord : true)
|
||||
}
|
||||
}
|
||||
panel (constraints : BorderLayout.SOUTH) {
|
||||
|
Reference in New Issue
Block a user