From 582baafef089a8fac7758581d29b633fac7aaf31 Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 30 Jul 2020 18:21:25 +0200 Subject: [PATCH] speedtest: few fixes --- .../jniadapter/test/speedtest/MTMsgCodec.java | 480 ++++++++++++++++++ .../jniadapter/test/speedtest/SpeedTest.java | 3 + 2 files changed, 483 insertions(+) create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java new file mode 100644 index 0000000..f1b96d3 --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/MTMsgCodec.java @@ -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. + * + *

+ * See SWIFT message structure, MT message format, MT999 + *

+ *

+ * Copyright 2019, p≡p + * Security. + *

+ * + * @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 (?.*?)-----\n" + "(\\w:.*?$\n)*" + "\n" + + "(?.*?)" + "-----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/(?\\w+)"; + private final static String uri_regex = "(?\\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 pgp_text 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 payload 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 result = new Vector(); + 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 longmsg; 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 EncFormat is other than + * None, Inline + * or PEP + */ + + 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 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 _to = new Vector(); + _to.add(to); + m.setTo(_to); + + m.setDir(header.inputIdentifier.compareTo("I") == 0 ? Message.Direction.Incoming : Message.Direction.Outgoing); + + m.setLongmsg(txt); + + ArrayList> al = new ArrayList>(); + Pair field = new Pair("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 result = new Vector(); + + 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 a = new Vector(); + 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); + } +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java index b6d971a..d445023 100644 --- a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java @@ -83,6 +83,9 @@ public class SpeedTest { int deth = 1; int enth = 1; + int cores = Runtime.getRuntime().availableProcessors(); + System.out.println(String.format("Number of cores: %d", cores)); + MT999 testMessage = new MT999("232323232323", "424242424242", "O", "23", "", "Hello, world"); String testData = testMessage.toString();