mirror of https://github.com/zlatinb/muwire
[de]serialization of profile headers and profiles
parent
d3dcbba34c
commit
ece040be76
|
@ -6,6 +6,14 @@ public class Constants {
|
|||
public static final byte PERSONA_VERSION = (byte)1;
|
||||
public static final String INVALID_NICKNAME_CHARS = "'\"();<>=@$%";
|
||||
public static final int MAX_NICKNAME_LENGTH = 30;
|
||||
|
||||
public static final byte PROFILE_HEADER_VERSION = (byte)1;
|
||||
public static final int MAX_PROFILE_TITLE_LENGTH = 128;
|
||||
|
||||
public static final byte PROFILE_VERSION = (byte)1;
|
||||
public static final int MAX_PROFILE_IMAGE_LENGTH = 200 * 1024;
|
||||
public static final int MAX_PROFILE_LENGTH = 0x1 << 18;
|
||||
|
||||
public static final byte FILE_CERT_VERSION = (byte)2;
|
||||
public static final int CHAT_VERSION = 2;
|
||||
|
||||
|
@ -23,7 +31,6 @@ public class Constants {
|
|||
public static final long MAX_HEADER_TIME = 60 * 1000;
|
||||
|
||||
public static final int MAX_RESULTS = 0x1 << 20;
|
||||
public static final int MAX_PROFILE_LENGTH = 0x1 << 18;
|
||||
|
||||
public static final int MAX_COMMENT_LENGTH = 0x1 << 15;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.muwire.core;
|
||||
|
||||
class InvalidSignatureException extends Exception {
|
||||
public class InvalidSignatureException extends Exception {
|
||||
|
||||
public InvalidSignatureException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
|
|
@ -13,11 +13,11 @@ import java.nio.charset.StandardCharsets;
|
|||
public class Name {
|
||||
final String name;
|
||||
|
||||
Name(String name) {
|
||||
public Name(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Name(InputStream nameStream) throws IOException {
|
||||
public Name(InputStream nameStream) throws IOException {
|
||||
DataInputStream dis = new DataInputStream(nameStream);
|
||||
int length = dis.readUnsignedShort();
|
||||
byte [] nameBytes = new byte[length];
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
package com.muwire.core.profile;
|
||||
|
||||
import com.muwire.core.Constants;
|
||||
import com.muwire.core.InvalidNicknameException;
|
||||
import com.muwire.core.InvalidSignatureException;
|
||||
import com.muwire.core.Name;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.data.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class MWProfile {
|
||||
|
||||
private final byte version;
|
||||
private final MWProfileHeader header;
|
||||
private final byte[] image;
|
||||
private final MWProfileImageFormat format;
|
||||
private final Name body;
|
||||
private final byte [] sig;
|
||||
|
||||
private volatile byte[] payload;
|
||||
private volatile String base64;
|
||||
|
||||
public MWProfile(InputStream inputStream) throws IOException, DataFormatException,
|
||||
InvalidSignatureException, InvalidNicknameException {
|
||||
version = (byte) (inputStream.read() & 0xFF);
|
||||
if (version != Constants.PROFILE_VERSION)
|
||||
throw new IOException("unknown version " + version);
|
||||
|
||||
header = new MWProfileHeader(inputStream);
|
||||
|
||||
DataInputStream dais = new DataInputStream(inputStream);
|
||||
|
||||
byte imageFormat = dais.readByte();
|
||||
if (imageFormat == 0)
|
||||
format = MWProfileImageFormat.PNG;
|
||||
else if (imageFormat == 1)
|
||||
format = MWProfileImageFormat.JPG;
|
||||
else
|
||||
throw new IOException("unknown image format for " + header.getPersona().getHumanReadableName() + " " + imageFormat);
|
||||
|
||||
int imageLength = dais.readInt();
|
||||
if (imageLength > Constants.MAX_PROFILE_IMAGE_LENGTH)
|
||||
throw new IOException("image too long for " + header.getPersona().getHumanReadableName() + " " + imageLength);
|
||||
image = new byte[imageLength];
|
||||
dais.readFully(image);
|
||||
|
||||
body = new Name(dais);
|
||||
if (body.getName().length() > Constants.MAX_COMMENT_LENGTH)
|
||||
throw new IOException("body too long for " + header.getPersona().getHumanReadableName() + " " + body.getName().length());
|
||||
|
||||
sig = new byte[Constants.SIG_TYPE.getSigLen()];
|
||||
dais.readFully(sig);
|
||||
|
||||
if (!verify())
|
||||
throw new InvalidSignatureException("Profile for " + header.getPersona().getHumanReadableName() + " did not verify");
|
||||
}
|
||||
|
||||
public MWProfile(MWProfileHeader header, byte[] image, MWProfileImageFormat format, String body, SigningPrivateKey spk)
|
||||
throws IOException, DataFormatException {
|
||||
this.version = Constants.PROFILE_VERSION;
|
||||
this.header = header;
|
||||
this.format = format;
|
||||
this.image = image;
|
||||
this.body = new Name(body);
|
||||
|
||||
byte [] signablePayload = signablePayload();
|
||||
Signature signature = DSAEngine.getInstance().sign(signablePayload, spk);
|
||||
this.sig = signature.getData();
|
||||
}
|
||||
|
||||
private byte[] signablePayload() throws IOException, DataFormatException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream daos = new DataOutputStream(baos);
|
||||
|
||||
daos.write(version);
|
||||
header.write(daos);
|
||||
|
||||
daos.write((byte) format.ordinal());
|
||||
|
||||
daos.writeInt(image.length);
|
||||
daos.write(image);
|
||||
|
||||
body.write(daos);
|
||||
|
||||
daos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private boolean verify() throws IOException, DataFormatException {
|
||||
byte [] payload = signablePayload();
|
||||
SigningPublicKey spk = header.getPersona().getDestination().getSigningPublicKey();
|
||||
Signature signature = new Signature(spk.getType(), sig);
|
||||
return DSAEngine.getInstance().verifySignature(signature, payload, spk);
|
||||
}
|
||||
|
||||
public void write(OutputStream outputStream) throws IOException, DataFormatException {
|
||||
if (payload == null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write(signablePayload());
|
||||
baos.write(sig);
|
||||
payload = baos.toByteArray();
|
||||
}
|
||||
outputStream.write(payload);
|
||||
}
|
||||
|
||||
public String toBase64() {
|
||||
if (base64 == null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
write(baos);
|
||||
} catch (Exception impossible) {
|
||||
throw new RuntimeException(impossible);
|
||||
}
|
||||
base64 = Base64.encode(baos.toByteArray());
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
public MWProfileHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public MWProfileImageFormat getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public byte[] getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body.getName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package com.muwire.core.profile;
|
||||
|
||||
import com.muwire.core.*;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.data.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class MWProfileHeader {
|
||||
|
||||
private final byte version;
|
||||
private final Persona persona;
|
||||
private final byte[] thumbNail;
|
||||
private final Name title;
|
||||
private final byte[] sig;
|
||||
|
||||
private volatile String base64;
|
||||
private volatile byte[] payload;
|
||||
|
||||
public MWProfileHeader(InputStream inputStream) throws IOException, DataFormatException,
|
||||
InvalidSignatureException, InvalidNicknameException {
|
||||
version = (byte) (inputStream.read() & 0xFF);
|
||||
if (version != Constants.PROFILE_HEADER_VERSION)
|
||||
throw new IOException("unknown version " + version);
|
||||
|
||||
persona = new Persona(inputStream);
|
||||
|
||||
DataInputStream dis = new DataInputStream(inputStream);
|
||||
int thumbnailLength = dis.readUnsignedShort();
|
||||
thumbNail = new byte[thumbnailLength];
|
||||
dis.readFully(thumbNail);
|
||||
|
||||
title = new Name(dis);
|
||||
if (title.getName().length() > Constants.MAX_PROFILE_TITLE_LENGTH)
|
||||
throw new IOException("Profile title too long " + title.getName().length());
|
||||
|
||||
sig = new byte[Constants.SIG_TYPE.getSigLen()];
|
||||
dis.readFully(sig);
|
||||
|
||||
if (!verify())
|
||||
throw new InvalidSignatureException("Profile header for " + persona.getHumanReadableName() + " did not verify");
|
||||
}
|
||||
|
||||
public MWProfileHeader(Persona persona, byte [] thumbNail, String title, SigningPrivateKey spk)
|
||||
throws IOException, DataFormatException {
|
||||
this.version = Constants.PROFILE_HEADER_VERSION;
|
||||
this.persona = persona;
|
||||
this.thumbNail = thumbNail;
|
||||
this.title = new Name(title);
|
||||
|
||||
byte [] signablePayload = signablePayload();
|
||||
Signature signature = DSAEngine.getInstance().sign(signablePayload, spk);
|
||||
this.sig = signature.getData();
|
||||
}
|
||||
|
||||
private byte[] signablePayload() throws IOException, DataFormatException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream daos = new DataOutputStream(baos);
|
||||
|
||||
daos.write(version);
|
||||
persona.write(daos);
|
||||
daos.writeShort((short) thumbNail.length);
|
||||
daos.write(thumbNail);
|
||||
title.write(daos);
|
||||
daos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
private boolean verify() throws IOException, DataFormatException {
|
||||
byte [] payload = signablePayload();
|
||||
SigningPublicKey spk = persona.getDestination().getSigningPublicKey();
|
||||
Signature signature = new Signature(spk.getType(), sig);
|
||||
return DSAEngine.getInstance().verifySignature(signature, payload, spk);
|
||||
}
|
||||
|
||||
public void write(OutputStream outputStream) throws IOException, DataFormatException {
|
||||
if (payload == null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream daos = new DataOutputStream(baos);
|
||||
daos.write(signablePayload());
|
||||
daos.write(sig);
|
||||
daos.close();
|
||||
payload = baos.toByteArray();
|
||||
}
|
||||
outputStream.write(payload);
|
||||
}
|
||||
|
||||
public String toBase64() {
|
||||
if (base64 == null) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
write(baos);
|
||||
} catch (Exception impossible) {
|
||||
throw new RuntimeException(impossible);
|
||||
}
|
||||
base64 = Base64.encode(baos.toByteArray());
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
public Persona getPersona() {
|
||||
return persona;
|
||||
}
|
||||
|
||||
public byte[] getThumbNail() {
|
||||
return thumbNail;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title.getName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.muwire.core.profile;
|
||||
|
||||
public enum MWProfileImageFormat {
|
||||
PNG, JPG
|
||||
}
|
Loading…
Reference in New Issue