forked from pEp.foundation/pEpJNIAdapter
commit
175519edd2
@ -0,0 +1,194 @@
|
||||
package foundation.pEp.jniadapter.test.speedtest;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.regex.*;
|
||||
|
||||
/**
|
||||
* MT999 is a Free Format Message.
|
||||
*/
|
||||
|
||||
public class MT999 extends SWIFTMsg {
|
||||
|
||||
String trn;
|
||||
String rr;
|
||||
String narrative;
|
||||
|
||||
/**
|
||||
* Construct MT999 message by parsing.
|
||||
*
|
||||
* @param txt text to parse
|
||||
* @throws ParseException if not a valid MT999 message
|
||||
*/
|
||||
|
||||
public MT999(String txt) throws ParseException {
|
||||
Matcher m = MTConstants.mt999_pattern.matcher(txt);
|
||||
if (!m.matches())
|
||||
throw new ParseException("not a valid MT999 message", 0);
|
||||
|
||||
// retrieve Basic Header and Application Header fields
|
||||
retrieveHeader(m);
|
||||
|
||||
// no User Header Block
|
||||
|
||||
// Text Block
|
||||
trn = m.group("trn");
|
||||
rr = m.group("rr");
|
||||
narrative = m.group("narrative");
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct MT999 message.
|
||||
*
|
||||
* @param srcAddress is sender's address
|
||||
* @param dstAddress is receiver's address
|
||||
* @param dir <code>"I"</code> for incoming message <code>"O"</code> for
|
||||
* outgoing message
|
||||
* @param id transaction id
|
||||
* @param payload freeform text
|
||||
* @param session session number
|
||||
* @param sequence sequence number
|
||||
*/
|
||||
|
||||
public MT999(String srcAddress, String dstAddress, String dir, String id, String relref, String payload,
|
||||
String session, String sequence) {
|
||||
|
||||
if (srcAddress.length() != 12)
|
||||
throw new IllegalArgumentException("srcAddress must be 12 characters");
|
||||
if (dstAddress.length() != 12)
|
||||
throw new IllegalArgumentException("dstAddress must be 12 characters");
|
||||
if (dir.compareTo("I") != 0 && dir.compareTo("O") != 0)
|
||||
throw new IllegalArgumentException("dir must be I or O");
|
||||
if (id.length() < 1 || id.length() > 16)
|
||||
throw new IllegalArgumentException("id must be between 1 and 12 characters");
|
||||
if (relref.length() > 16)
|
||||
throw new IllegalArgumentException("related reference must be at most 16 characters");
|
||||
if (payload.length() < 1)
|
||||
throw new IllegalArgumentException("payload must have a value");
|
||||
|
||||
if (session.length() != 4)
|
||||
throw new IllegalArgumentException("session must be 4 digits");
|
||||
if (sequence.length() != 6)
|
||||
throw new IllegalArgumentException("sequence must be 6 digits");
|
||||
|
||||
mt999();
|
||||
|
||||
logicalTerminalAddress = srcAddress;
|
||||
destinationAddress = dstAddress;
|
||||
inputIdentifier = dir;
|
||||
|
||||
trn = id;
|
||||
rr = relref;
|
||||
narrative = payload;
|
||||
|
||||
sessionNumber = session;
|
||||
sequenceNumber = sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct MT999 message with defaults for session and sequence.
|
||||
*
|
||||
* @param srcAddress is sender's address
|
||||
* @param dstAddress is receiver's address
|
||||
* @param dir <code>"I"</code> for incoming message <code>"O"</code> for
|
||||
* outgoing message
|
||||
* @param id transaction id
|
||||
* @param payload freeform text
|
||||
*/
|
||||
|
||||
public MT999(String srcAddress, String dstAddress, String dir, String id, String relref, String payload) {
|
||||
|
||||
|
||||
if (srcAddress.length() != 12)
|
||||
throw new IllegalArgumentException("srcAddress must be 12 characters");
|
||||
if (dstAddress.length() != 12)
|
||||
throw new IllegalArgumentException("dstAddress must be 12 characters");
|
||||
if (dir.compareTo("I") != 0 && dir.compareTo("O") != 0)
|
||||
throw new IllegalArgumentException("dir must be I or O");
|
||||
if (id.length() < 1 || id.length() > 16)
|
||||
throw new IllegalArgumentException("id must be between 1 and 12 characters");
|
||||
if (relref.length() > 16)
|
||||
throw new IllegalArgumentException("related reference must be at most 16 characters");
|
||||
if (payload.length() < 1)
|
||||
throw new IllegalArgumentException("payload must have a value");
|
||||
|
||||
mt999();
|
||||
|
||||
logicalTerminalAddress = srcAddress;
|
||||
destinationAddress = dstAddress;
|
||||
inputIdentifier = dir;
|
||||
|
||||
trn = id;
|
||||
rr = relref;
|
||||
narrative = payload;
|
||||
|
||||
sessionNumber = "0000";
|
||||
sequenceNumber = "000000";
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert MT999 message by composing.
|
||||
*
|
||||
* @param fmt rendering format
|
||||
* @return string representation of MT999 message
|
||||
*/
|
||||
|
||||
public String toString(MTConstants.Format fmt) {
|
||||
String result;
|
||||
// FIXME: rework to stringbuffer
|
||||
switch (fmt) {
|
||||
case MTFIN:
|
||||
// Basic Header Block
|
||||
result = "{1:";
|
||||
result += applicationIdentifier;
|
||||
result += serviceIdentifier;
|
||||
result += logicalTerminalAddress;
|
||||
result += sessionNumber;
|
||||
result += sequenceNumber;
|
||||
result += "}";
|
||||
|
||||
// Application Header Block
|
||||
result += "{2:";
|
||||
result += inputIdentifier;
|
||||
result += messageType;
|
||||
result += destinationAddress;
|
||||
result += messagePriority;
|
||||
result += "}";
|
||||
|
||||
// no User Header Block
|
||||
|
||||
// Text Block
|
||||
result += "{4:\n";
|
||||
result += ":20:" + trn + "\n";
|
||||
if (rr != null && rr.length() > 0)
|
||||
result += ":21:" + rr +"\n";
|
||||
result += ":79:\n" + narrative;
|
||||
result += "\n-}";
|
||||
return result;
|
||||
|
||||
case MTXML:
|
||||
throw new UnsupportedOperationException("MTXML not yet implemented");
|
||||
}
|
||||
|
||||
throw new AssertionError("this should never happen");
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert MT999 message by composing.
|
||||
*
|
||||
* @return string representation of MT999 message
|
||||
*/
|
||||
|
||||
public String toString() {
|
||||
return toString(MTConstants.Format.MTFIN);
|
||||
}
|
||||
|
||||
// MT999 specifica
|
||||
|
||||
private void mt999() {
|
||||
applicationIdentifier = "F"; // FIN
|
||||
serviceIdentifier = "01"; // FIN
|
||||
messageType = "999";
|
||||
messagePriority = "N";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package foundation.pEp.jniadapter.test.speedtest;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MTConstants {
|
||||
|
||||
/**
|
||||
* Rendering Format of a SWIFT message.
|
||||
*/
|
||||
|
||||
enum Format {
|
||||
MTFIN, MTXML
|
||||
}
|
||||
|
||||
/**
|
||||
* SWIFTMessages have a Basic Header Block and an Application Header Block
|
||||
*/
|
||||
|
||||
static final String mt_regex = "(?ms)"
|
||||
// Basic Header Block
|
||||
+ "\\{1:" //
|
||||
+ "(?<ai>\\w)" //
|
||||
+ "(?<si>\\d{2})" //
|
||||
+ "(?<lta>\\w{12})" //
|
||||
+ "(?<sn>\\d{4})" //
|
||||
+ "(?<sqn>\\d{5,6})" //
|
||||
+ "\\}"
|
||||
|
||||
// Application Header Block
|
||||
+ "\\{2:" //
|
||||
+ "(?<ii>I|O)" //
|
||||
+ "(?<mt>\\d{3})" //
|
||||
+ "(\\d{10})?" //
|
||||
+ "(?<da>\\w{12})" //
|
||||
+ "(.*)?" //
|
||||
+ "(?<mp>U|N|S)" //
|
||||
+ "\\}"
|
||||
|
||||
// FIXME: User Header Block (the wrong way). The trailing .* is to make it useable for basic swift and mt999 ...
|
||||
+ "(\\{3:?(.*)\\})?(.*)" //
|
||||
;
|
||||
|
||||
static final Pattern mt_pattern = Pattern.compile(mt_regex);
|
||||
|
||||
static final String mt999_regex = mt_regex
|
||||
// Text Block
|
||||
+ "\\{4:\n" //
|
||||
+ ":20:(?<trn>\\w{1,16})\n" //
|
||||
+ "(:21:(?<rr>.{1,16})\n)?" //
|
||||
+ ":79:(?<narrative>.*?)" //
|
||||
+ "\n-\\}" //
|
||||
// trailer
|
||||
+ ".*"
|
||||
|
||||
;
|
||||
|
||||
static final Pattern mt999_pattern = Pattern.compile(mt999_regex);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,480 @@
|
||||
package foundation.pEp.jniadapter.test.speedtest;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import foundation.pEp.jniadapter.Blob;
|
||||
import foundation.pEp.jniadapter.Engine;
|
||||
import foundation.pEp.jniadapter.Identity;
|
||||
import foundation.pEp.jniadapter.Message;
|
||||
import foundation.pEp.jniadapter.Pair;
|
||||
|
||||
/**
|
||||
* MTMsgCodec is the Codec class for encoding/decoding p≡p for SWIFT messages.
|
||||
*
|
||||
* <p>
|
||||
* See <a href=
|
||||
* "https://www.sepaforcorporates.com/swift-for-corporates/read-swift-message-structure/"
|
||||
* target="_blank">SWIFT message structure</a>, <a href=
|
||||
* "https://www.ibm.com/support/knowledgecenter/SSRH32_3.0.0_SWS/mt_msg_format.html"
|
||||
* target="_blank">MT message format</a>, <a href=
|
||||
* "http://www.iotafinance.com/en/SWIFT-ISO15022-Message-type-MT999.html" target
|
||||
* ="_blank">MT999</a>
|
||||
* </p>
|
||||
* <p>
|
||||
* Copyright 2019, <a href="https://pep.security" target="_blank">p≡p
|
||||
* Security</a>.
|
||||
* </p>
|
||||
*
|
||||
* @author Volker Birk
|
||||
* @version %I% %G%
|
||||
*/
|
||||
|
||||
// FIXME: rework to stringbuffer
|
||||
|
||||
public class MTMsgCodec {
|
||||
public Engine pEp;
|
||||
|
||||
/**
|
||||
* Constructs a MessageCodec using p≡p engine.
|
||||
*
|
||||
* @param pEp_engine p≡p engine to use
|
||||
*/
|
||||
|
||||
public MTMsgCodec(Engine pEp_engine) {
|
||||
pEp = pEp_engine;
|
||||
}
|
||||
|
||||
private final static String magicKeys = "pEpKeys";
|
||||
private final static String magicEnc = "pEpMessage";
|
||||
|
||||
private final static String pgp_regex = "(?ms)" + "-----BEGIN PGP (?<type>.*?)-----\n" + "(\\w:.*?$\n)*" + "\n"
|
||||
+ "(?<block>.*?)" + "-----END PGP (.*?)-----.*";
|
||||
|
||||
private static final Pattern pgp_pattern = Pattern.compile(pgp_regex);
|
||||
|
||||
private static final String pgptypeMessage = "MESSAGE";
|
||||
private static final String pgptypePubkey = "PUBLIC KEY BLOCK";
|
||||
|
||||
// FIXME: private final static String uri_regex = "payto://swift/(?<bic>\\w+)";
|
||||
private final static String uri_regex = "(?<bic>\\w+)@BIC";
|
||||
|
||||
private final static Pattern uri_pattern = Pattern.compile(uri_regex);
|
||||
|
||||
/**
|
||||
* Strips PGP header and footer from encryption or key data.
|
||||
*
|
||||
* @param pgp_text text to work on
|
||||
* @throws ParseException if text is not valid PGP data
|
||||
* @return <code>pgp_text</code> without PGP header
|
||||
*/
|
||||
|
||||
protected static String stripFromPGP(String pgp_text) throws ParseException {
|
||||
|
||||
Matcher m = pgp_pattern.matcher(pgp_text);
|
||||
if (!m.matches())
|
||||
throw new ParseException("not a PGP block", 0);
|
||||
|
||||
return m.group("block");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds PGP header and footer.
|
||||
*
|
||||
* @param payload text to decorate
|
||||
* @param pgp_type PGP data type
|
||||
* @return <code>payload</code> with added header and footer
|
||||
*/
|
||||
|
||||
protected static String addPGPHeader(String payload, String pgp_type) {
|
||||
// FIXME: rework to stringbuffer
|
||||
return "-----BEGIN PGP " + pgp_type + "-----\n\n" + payload + "\n-----END PGP " + pgp_type + "-----\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a BIC from an URI.
|
||||
*
|
||||
* @param uri the URI to decode from
|
||||
* @throws ParseException if URI has not the correct form
|
||||
* @return decoded BIC
|
||||
*/
|
||||
|
||||
public static String decodeBIC(String uri) throws ParseException {
|
||||
Matcher m = uri_pattern.matcher(uri);
|
||||
if (!m.matches())
|
||||
throw new ParseException("not a valid URI", 0);
|
||||
|
||||
return m.group("bic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a BIC into an URI.
|
||||
*
|
||||
* @param bic BIC to encode
|
||||
* @return encoded URI
|
||||
*/
|
||||
|
||||
public static String encodeBIC(String bic) {
|
||||
// return "payto://swift/" + bic
|
||||
return bic + "@BIC";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a list of transporting MT999 messages for a payload.
|
||||
*
|
||||
* @param from source address
|
||||
* @param to destination address
|
||||
* @param ii message direction
|
||||
* @param trn message id
|
||||
* @param payload payload to split
|
||||
* @param magic magic string to mark as p≡p message
|
||||
*
|
||||
* @return array of String with MT999 messages
|
||||
*/
|
||||
|
||||
protected String[] transportMT999(String from, String to, String ii, String trn, String rr, String payload,
|
||||
String magic) {
|
||||
|
||||
Vector<String> result = new Vector<String>();
|
||||
payload = payload.trim();
|
||||
|
||||
int j = 1, f = 0, t = 0;
|
||||
for (int i = payload.indexOf("\n"); i != -1; i = payload.indexOf("\n", i + 1), j++) {
|
||||
|
||||
if (j % 34 == 0) {
|
||||
t = i + 1;
|
||||
String cont = t < payload.length() - 1 ? "." : "";
|
||||
MT999 mt999 = new MT999(from, to, ii, trn, rr, magic + "\n" + payload.substring(f, t) + cont);
|
||||
result.add(mt999.toString());
|
||||
f = i + 1;
|
||||
}
|
||||
}
|
||||
if (t < payload.length() - 1) {
|
||||
int z = payload.charAt(payload.length() - 1) == '\n' ? 1 : 0;
|
||||
MT999 mt999 = new MT999(from, to, ii, "23", // fixed trn
|
||||
"", magic + "\n" + payload.substring(t, payload.length() - z));
|
||||
result.add(mt999.toString());
|
||||
}
|
||||
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* encodes a p≡p Message to a String with a serialized p≡p for SWIFT message.
|
||||
*
|
||||
* @param msg p≡p Message to encode if from or to are not set they're taken
|
||||
* by parsing the header of <code>longmsg</code>; in this case
|
||||
* message direction is being taken from there, too
|
||||
* @param config properties with configuration
|
||||
* @return String with encoded p≡p for SWIFT message
|
||||
* @throws UnsupportedOperationException if <code>EncFormat</code> is other than
|
||||
* <code>None</code>, <code>Inline</code>
|
||||
* or <code>PEP</code>
|
||||
*/
|
||||
|
||||
public String encode(Message msg, Properties config) {
|
||||
if (msg.getLongmsg() == null || msg.getLongmsg().length() < 1)
|
||||
throw new IllegalArgumentException("longmsg must contain the message");
|
||||
|
||||
String result = "";
|
||||
|
||||
String msgid = msg.getId() != null && msg.getId().length() > 0 ? msg.getId() : "23";
|
||||
String dir = msg.getDir() == Message.Direction.Incoming ? "I" : "O";
|
||||
|
||||
if (msgid.length() > 12) {
|
||||
if (msgid.substring(0, 3).compareTo("pEp.") == 0 && msgid.length() >= 15)
|
||||
msgid = msgid.substring(4, 15);
|
||||
else
|
||||
msgid = msgid.substring(0, 11);
|
||||
}
|
||||
|
||||
String from = msg.getFrom() != null && msg.getFrom().address.length() > 0 ? msg.getFrom().address : null;
|
||||
|
||||
String to = msg.getTo() != null && msg.getTo().size() > 0 && msg.getTo().elementAt(0).address.length() > 0
|
||||
? msg.getTo().elementAt(0).address
|
||||
: null;
|
||||
|
||||
if (from != null) {
|
||||
try {
|
||||
from = decodeBIC(from);
|
||||
if (from.length() != 12)
|
||||
throw new IllegalArgumentException("from address must be URI with BIC12");
|
||||
} catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("from address must be URI with BIC12");
|
||||
}
|
||||
}
|
||||
|
||||
if (to != null) {
|
||||
try {
|
||||
to = decodeBIC(to);
|
||||
if (to.length() != 12)
|
||||
throw new IllegalArgumentException("to address must be URI with BIC12");
|
||||
} catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("to address must be URI with BIC12");
|
||||
}
|
||||
}
|
||||
|
||||
switch (msg.getEncFormat()) {
|
||||
case None:
|
||||
result = msg.getLongmsg(); // we send the message unmodified
|
||||
if (result.substring(0, 3).compareTo("{1:") == 0) {
|
||||
// probably SWIFT MTFIN
|
||||
|
||||
if (from == null || to == null || msgid == null) {
|
||||
// parse SWIFT header
|
||||
SWIFTMsg mh = new SWIFTMsg();
|
||||
|
||||
try {
|
||||
mh.parseHeader(result);
|
||||
} catch (ParseException ex) {
|
||||
throw new UnsupportedOperationException("unsupported message format");
|
||||
}
|
||||
|
||||
if (from == null)
|
||||
from = mh.logicalTerminalAddress;
|
||||
if (to == null)
|
||||
to = mh.destinationAddress;
|
||||
dir = mh.inputIdentifier;
|
||||
}
|
||||
} else if (result.substring(0, 1).compareTo("<") == 0) {
|
||||
// probably XML
|
||||
|
||||
// FIXME: let's do the job for MTXML, too
|
||||
return result;
|
||||
} else { // we don't know this format, so let it be
|
||||
return result;
|
||||
}
|
||||
|
||||
if(msg.getAttachments() != null)
|
||||
for (int i = 0; i < msg.getAttachments().size(); ++i) { // FIXME: can attachments become null?
|
||||
Blob attach = msg.getAttachments().elementAt(i);
|
||||
String magic;
|
||||
|
||||
if (attach.mime_type.compareToIgnoreCase("application/pgp-keys") == 0)
|
||||
magic = magicKeys;
|
||||
else
|
||||
break; // don't know this MIME type
|
||||
|
||||
// we send an MT999 with the keys
|
||||
String payload;
|
||||
try {
|
||||
payload = stripFromPGP(new String(attach.data, StandardCharsets.UTF_8));
|
||||
} catch (ParseException ex) {
|
||||
// cannot parse this
|
||||
break;
|
||||
}
|
||||
|
||||
String[] msgs = transportMT999(from, to, dir, msgid, "", payload, magic);
|
||||
|
||||
for (int j = 0; j < msgs.length; ++j) {
|
||||
if (j > 0)
|
||||
result += "\n";
|
||||
result += msgs[j].toString();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PEP:
|
||||
case PGPMIME:
|
||||
case Inline:
|
||||
if (from == null || to == null)
|
||||
throw new IllegalArgumentException("from and to must be set to URIs with BIC12");
|
||||
|
||||
String pgp_txt;
|
||||
Vector<Blob> attachments = null;
|
||||
|
||||
if (msg.getEncFormat() == Message.EncFormat.PEP || msg.getEncFormat() == Message.EncFormat.PGPMIME) {
|
||||
if (msg.getAttachments() == null || msg.getAttachments().size() != 2)
|
||||
throw new IllegalArgumentException("no valid message format");
|
||||
Blob attach = msg.getAttachments().elementAt(1);
|
||||
pgp_txt = new String(attach.data, StandardCharsets.UTF_8);
|
||||
} else /* Inline */ {
|
||||
pgp_txt = msg.getLongmsg();
|
||||
}
|
||||
|
||||
String payload;
|
||||
try {
|
||||
payload = stripFromPGP(pgp_txt);
|
||||
} catch (ParseException ex) {
|
||||
// cannot parse this
|
||||
throw new IllegalArgumentException("illegal encryption text");
|
||||
}
|
||||
String[] msgs = transportMT999(from, to, dir, msgid, "", payload, magicEnc);
|
||||
|
||||
for (int j = 0; j < msgs.length; ++j) {
|
||||
if (j > 0)
|
||||
result += "\n";
|
||||
result += msgs[j].toString();
|
||||
}
|
||||
|
||||
if (msg.getEncFormat() == Message.EncFormat.Inline) {
|
||||
String[] attached_key = null;
|
||||
attachments = msg.getAttachments();
|
||||
|
||||
for (int i = 0; attachments != null && i < attachments.size(); ++i) {
|
||||
Blob attach = attachments.elementAt(i);
|
||||
// if (attach.mime_type.compareTo("application/pgp-keys") == 0) {
|
||||
// we send an MT999 with the keys
|
||||
try {
|
||||
payload = stripFromPGP(new String(attach.data, StandardCharsets.UTF_8));
|
||||
} catch (ParseException ex) {
|
||||
// cannot parse this
|
||||
break;
|
||||
}
|
||||
msgs = transportMT999(from, to, dir, msgid, "", payload, magicKeys);
|
||||
|
||||
for (int j = 0; j < msgs.length; ++j) {
|
||||
if (j > 0)
|
||||
result += "\n";
|
||||
result += msgs[j].toString();
|
||||
}
|
||||
// } else {
|
||||
// throw new UnsupportedOperationException(
|
||||
// "only application/pgp-keys is supported with Inline but got " + attach.mime_type);
|
||||
// }
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("unsupported encryption format");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates p≡p message from MTFIN or MTXML using SWIFT header info
|
||||
*
|
||||
* @param header MTFIN header structure
|
||||
* @param txt MTFIN message text
|
||||
* @return p≡p message
|
||||
*/
|
||||
|
||||
protected Message pEpMessageFromSWIFTMessage(SWIFTMsg header, String txt) {
|
||||
Message m = new Message();
|
||||
|
||||
Identity from = new Identity();
|
||||
from.address = encodeBIC(header.logicalTerminalAddress);
|
||||
m.setFrom(from);
|
||||
|
||||
Identity to = new Identity();
|
||||
to.address = encodeBIC(header.destinationAddress);
|
||||
Vector<Identity> _to = new Vector<Identity>();
|
||||
_to.add(to);
|
||||
m.setTo(_to);
|
||||
|
||||
m.setDir(header.inputIdentifier.compareTo("I") == 0 ? Message.Direction.Incoming : Message.Direction.Outgoing);
|
||||
|
||||
m.setLongmsg(txt);
|
||||
|
||||
ArrayList<Pair<String, String>> al = new ArrayList<Pair<String, String>>();
|
||||
Pair<String, String> field = new Pair<String, String>("X-pEp-Version", pEp.getProtocolVersion());
|
||||
al.add(field);
|
||||
m.setOptFields(al);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* decodes p≡p Messages from a String with serialized p≡p for SWIFT messages.
|
||||
*
|
||||
* @param txt String to decode from
|
||||
* @throws ParseException if the String does not contain SWIFT messages only
|
||||
* @return array with p≡p Messages
|
||||
*
|
||||
*/
|
||||
|
||||
public Message[] decode(String txt) throws ParseException {
|
||||
Vector<Message> result = new Vector<Message>();
|
||||
|
||||
if (txt.substring(0, 3).compareTo("{1:") == 0) {
|
||||
// probably SWIFT MTFIN
|
||||
|
||||
int f = 0;
|
||||
int t = txt.indexOf("{1:", 3);
|
||||
if (t == -1)
|
||||
t = txt.length();
|
||||
|
||||
String key_payload = "";
|
||||
String enc_payload = "";
|
||||
while (f < txt.length()) {
|
||||
String _txt = txt.substring(f, t);
|
||||
|
||||
f = t;
|
||||
t = txt.indexOf("{1:", f + 3);
|
||||
if (t == -1)
|
||||
t = txt.length();
|
||||
|
||||
Message last = null;
|
||||
|
||||
SWIFTMsg m = new SWIFTMsg();
|
||||
m.parseHeader(_txt);
|
||||
|
||||
boolean done = false;
|
||||
if (m.messageType.compareTo("999") == 0) {
|
||||
MT999 _m = new MT999(_txt);
|
||||
String narrative = _m.narrative.trim();
|
||||
if (narrative.substring(0, magicKeys.length()).compareTo(magicKeys) == 0) {
|
||||
key_payload += narrative.substring(magicKeys.length()).trim();
|
||||
if (key_payload.substring(key_payload.length()).compareTo(".") == 0) {
|
||||
key_payload = key_payload.substring(0, key_payload.length() - 2);
|
||||
} else {
|
||||
// p≡p keys
|
||||
String keydata = addPGPHeader(key_payload, pgptypePubkey);
|
||||
key_payload = "";
|
||||
|
||||
if (last == null)
|
||||
last = pEpMessageFromSWIFTMessage(m, "p≡p keys");
|
||||
|
||||
Vector<Blob> a = new Vector<Blob>();
|
||||
Blob b = new Blob();
|
||||
b.data = keydata.getBytes(StandardCharsets.UTF_8);
|
||||
b.mime_type = "application/pgp-keys";
|
||||
b.filename = "pEpKeys.asc";
|
||||
a.add(b);
|
||||
last.setAttachments(a);
|
||||
|
||||
result.add(last);
|
||||
last = null;
|
||||
|
||||
}
|
||||
done = true;
|
||||
} else if (narrative.substring(0, magicEnc.length()).compareTo(magicEnc) == 0) {
|
||||
// p≡p encrypted data
|
||||
enc_payload += narrative.substring(magicEnc.length()).trim();
|
||||
if (enc_payload.substring(enc_payload.length()).compareTo(".") == 0) {
|
||||
enc_payload = enc_payload.substring(0, enc_payload.length() - 2);
|
||||
} else {
|
||||
// p≡p encryption
|
||||
String encdata = addPGPHeader(enc_payload, pgptypeMessage);
|
||||
Message r = pEpMessageFromSWIFTMessage(m, encdata);
|
||||
r.setEncFormat(Message.EncFormat.Inline);
|
||||
result.add(r);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
last = pEpMessageFromSWIFTMessage(m, _txt);
|
||||
result.add(last);
|
||||
}
|
||||
}
|
||||
} else if (txt.substring(0, 1).compareTo("<") == 0) {
|
||||
// probably XML
|
||||
|
||||
// FIXME: let's do the job for MTXML, too
|
||||
throw new UnsupportedOperationException("XML not yet implemented");
|
||||
} else { // we don't know this format
|
||||
throw new ParseException("not a valid SWIFT message", 0);
|
||||
}
|
||||
|
||||
Message[] _result = new Message[result.size()];
|
||||
return result.toArray(_result);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
include ../../../../../../../Makefile.conf
|
||||
include ../Makefile.conf
|
||||
|
||||
TEST_UNIT_NAME=speedtest
|
||||
|
||||
JAVA_CLASSES = \
|
||||
MT999.class \
|
||||
MTConstants.class \
|
||||
MTMsgCodec.class \
|
||||
SpeedTest.class \
|
||||
SWIFTMsg.class
|
||||
|
||||
.PHONY: pitytest compile alice test clean
|
||||
|
||||
all: alice compile
|
||||
|
||||
pitytest:
|
||||
$(MAKE) -C $(PITYTEST_DIR)
|
||||
|
||||
alice: compile clean-pep-home-alice
|
||||
cd $(JAVA_CWD);pwd;HOME=$(JAVA_PEP_HOME_DIR_ALICE) $(JAVA) $(JAVA_PKG_BASENAME).$(TEST_UNIT_NAME).SpeedTest
|
||||
|
||||
compile: $(JAVA_CLASSES) pitytest
|
||||
|
||||
%.class: %.java
|
||||
cd $(JAVA_CWD);javac -cp $(CLASSPATH) $(JAVA_PKG_BASEPATH)/$(TEST_UNIT_NAME)/$<
|
||||
|
||||
clean:
|
||||
rm -f $(JAVA_CLASSES)
|
||||
rm -f *.class
|
||||
rm -f *.log
|
||||
rm -Rf .gnupg
|
||||
rm -Rf .lldb
|
||||
|
||||
clean-pep-home: clean-pep-home-alice
|
||||
|
||||
clean-pep-home-alice:
|
||||
rm -rf $(PEP_HOME_DIR_ALICE)/.pEp
|
@ -0,0 +1,58 @@
|
||||
package foundation.pEp.jniadapter.test.speedtest;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class SWIFTMsg {
|
||||
// Basic Header Block
|
||||
public String applicationIdentifier;
|
||||
public String serviceIdentifier;
|
||||
public String logicalTerminalAddress;
|
||||
public String sessionNumber;
|
||||
public String sequenceNumber;
|
||||
|
||||
// Application Header Block
|
||||
public String inputIdentifier;
|
||||
public String messageType;
|
||||
public String destinationAddress;
|
||||
public String messagePriority;
|
||||
|
||||
/**
|
||||
* retrieve header fields of Basic Header Block and Application Header
|
||||
* Block
|
||||
*
|
||||
* @param m Matcher object of a regex result
|
||||
*/
|
||||
|
||||
public void retrieveHeader(Matcher m) {
|
||||
// Basic Header Block
|
||||
applicationIdentifier = m.group("ai");
|
||||
serviceIdentifier = m.group("si");
|
||||
logicalTerminalAddress = m.group("lta");
|
||||
sessionNumber = m.group("sn");
|
||||
sequenceNumber = m.group("sqn");
|
||||
|
||||
// Application Header Block
|
||||
inputIdentifier = m.group("ii");
|
||||
messageType = m.group("mt");
|
||||
destinationAddress = m.group("da");
|
||||
messagePriority = m.group("mp");
|
||||
}
|
||||
|
||||
/**
|
||||
* parse MTFIN header and retrieve header fields into variables.
|
||||
*
|
||||
* @param txt MTxxx message to parse
|
||||
* @throws ParseException if not a valid MTxxx message
|
||||
*/
|
||||
|
||||
public void parseHeader(String txt) throws ParseException {
|
||||
// String header = txt.substring(0, 50);
|
||||
|
||||
Matcher m = MTConstants.mt_pattern.matcher(txt);
|
||||
if (!m.matches())
|
||||
throw new ParseException("not a valid MTxxx message", 0);
|
||||
|
||||
retrieveHeader(m);
|
||||
}
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
package foundation.pEp.jniadapter.test.speedtest;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Vector;
|
||||
import java.util.Scanner;
|
||||
|
||||
import foundation.pEp.jniadapter.*;
|
||||
|
||||
public class SpeedTest {
|
||||
private static Engine pEp = new Engine();
|
||||
private static MTMsgCodec codec = new MTMsgCodec(pEp);
|
||||
private static Identity me = new Identity(true);
|
||||
private static Identity you = new Identity();
|
||||
|
||||
private static long decodingCount = 0;
|
||||
private static long encodingCount = 0;
|
||||
private static int deth = 0;
|
||||
private static int enth = 0;
|
||||
|
||||
private static String testData = null;
|
||||
|
||||
|
||||
protected static void decodingTest(Engine eng, long n, String testDataEnc) {
|
||||
for (long i = 0; i < n; ++i) {
|
||||
try {
|
||||
Message[] msgs = codec.decode(testDataEnc);
|
||||
Vector<String> keys = new Vector<String>();
|
||||
Engine.decrypt_message_Return ret = eng.decrypt_message(msgs[0], keys, 0);
|
||||
String txt = ret.dst.getLongmsg();
|
||||
} catch (ParseException ex) {
|
||||
System.err.println("error: parsing test data");
|
||||
System.exit(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class DecodingThread extends Thread {
|
||||
private long _n;
|
||||
private String _testDataEnc;
|
||||
private Engine _localpEp;
|
||||
|
||||
public DecodingThread(long n, String testDataEnc) {
|
||||
_n = n;
|
||||
_testDataEnc = testDataEnc;
|
||||
_localpEp = new Engine();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
decodingTest(_localpEp, _n, _testDataEnc);
|
||||
}
|
||||
}
|
||||
|
||||
private static Message encrypt(Engine eng, String data) {
|
||||
Message m = new Message();
|
||||
m.setDir(Message.Direction.Outgoing);
|
||||
m.setFrom(me);
|
||||
Vector<Identity> to = new Vector<Identity>();
|
||||
to.add(you);
|
||||
m.setTo(to);
|
||||
m.setLongmsg(data);
|
||||
return eng.encrypt_message(m, null, Message.EncFormat.Inline);
|
||||
}
|
||||
|
||||
protected static void encodingTest(Engine eng, long n, String testData) {
|
||||
for (long i = 0; i < n; ++i) {
|
||||
Message enc = encrypt(eng, testData);
|
||||
String txt = codec.encode(enc, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected class EncodingThread extends Thread {
|
||||
private long _n;
|
||||
private String _testData;
|
||||
private Engine _localpEp;
|
||||
|
||||
public EncodingThread(long n, String testData) {
|
||||
_n = n;
|
||||
_testData = testData;
|
||||
_localpEp = new Engine();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
encodingTest(_localpEp, _n, _testData);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseOpts(String[] args) {
|
||||
for (int i = 0; i < args.length; ++i) {
|
||||
if (args[i].compareTo("-h") == 0 || args[i].compareTo("--help") == 0) {
|
||||
System.out.println("SpeedTest [-e |--encode NUMBER] [-d | --decode NUMBER] [-f | --file TESTDATA] [-jd | --decoding-threads DT] [-je | --encoding-threads] [-h | --help]\n"
|
||||
+ "\nEncodes and/or decodes messages to measure the speed.\n\n"
|
||||
+ " -d, --decode NUMBER decode NUMBER messages per thread\n"
|
||||
+ " -e, --encode NUMBER encode NUMBER messages per thread\n"
|
||||
+ " -f, --file TESTDATA file with test data as UTF-8 encoded text\n"
|
||||
+ " -jd, --decoding-threads DT starting DT threads for decoding\n"
|
||||
+ " -je, --encoding-threads ET starting ET threads for encoding\n"
|
||||
+ " -h, --help show this help message\n"
|
||||
+ "\nThis program encrypts and encodes, and decrypts and decodes test data\n"
|
||||
+ "NUMBER times, respectively. If you omit -f it will encode a default data set.\n"
|
||||
);
|
||||
System.exit(0);
|
||||
} else if (args[i].compareTo("-d") == 0 || args[i].compareTo("--decode") == 0) {
|
||||
try {
|
||||
decodingCount = Long.parseLong(args[i + 1]);
|
||||
++i;
|
||||
} catch (NumberFormatException ex) {
|
||||
System.err.println(String.format("error: decimal number expected but found %s", args[i + 1]));
|
||||
System.exit(1);
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i]));
|
||||
System.exit(1);
|
||||
}
|
||||
} else if (args[i].compareTo("-e") == 0 || args[i].compareTo("--encode") == 0) {
|
||||
try {
|
||||
encodingCount = Long.parseLong(args[i + 1]);
|
||||
++i;
|
||||
} catch (NumberFormatException ex) {
|
||||
System.err.println(String.format("error: decimal number expected but found %s", args[i + 1]));
|
||||
System.exit(1);
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i]));
|
||||
System.exit(1);
|
||||
}
|
||||
} else if (args[i].compareTo("-f") == 0 || args[i].compareTo("--file") == 0) {
|
||||
String filename = "";
|
||||
|
||||
try {
|
||||
filename = args[i + 1];
|
||||
++i;
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
System.err.println(String.format("error: %s is requiring a filename as argument", args[i]));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
if (filename.compareTo("-") == 0) {
|
||||
Scanner s = new Scanner(System.in).useDelimiter("\\A");
|
||||
testData = s.hasNext() ? s.next() : "";
|
||||
} else {
|
||||
testData = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
System.err.println(String.format("error: cannot read file %s", args[i]));
|
||||
System.exit(2);
|
||||
}
|
||||
} else if (args[i].compareTo("-jd") == 0 || args[i].compareTo("----decoding-threads") == 0) {
|
||||
try {
|
||||
deth = Integer.parseInt(args[i + 1]);
|
||||
++i;
|
||||
} catch (NumberFormatException ex) {
|
||||
System.err.println(String.format("error: decimal number expected but found %s", args[i + 1]));
|
||||
System.exit(1);
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i]));
|
||||
System.exit(1);
|
||||
}
|
||||
} else if (args[i].compareTo("-je") == 0 || args[i].compareTo("----encoding-threads") == 0) {
|
||||
try {
|
||||
enth = Integer.parseInt(args[i + 1]);
|
||||
++i;
|
||||
} catch (NumberFormatException ex) {
|
||||
System.err.println(String.format("error: decimal number expected but found %s", args[i + 1]));
|
||||
System.exit(1);
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
System.err.println(String.format("error: %s is requiring a decimal number as argument", args[i]));
|
||||
System.exit(1);
|
||||
}
|
||||
} else {
|
||||
System.err.println(String.format("illegal parameter: %s", args[i]));
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Initializing...");
|
||||