From f850eba355deeed3baa92af01157c0204117514d Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 30 Jul 2020 10:52:08 +0200 Subject: [PATCH 01/10] tests: add original speedtest from pEp_for_SWIFT repo (by fdik) --- .../pEp/jniadapter/test/speedtest/MT999.java | 194 +++++++ .../test/speedtest/MTConstants.java | 60 +++ .../jniadapter/test/speedtest/MtMsgCodec.java | 480 ++++++++++++++++++ .../jniadapter/test/speedtest/SpeedTest.java | 275 ++++++++++ .../jniadapter/test/speedtest/SwiftMSG.java | 58 +++ .../jniadapter/test/speedtest/speedtest.sh | 10 + 6 files changed, 1077 insertions(+) create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/MtMsgCodec.java create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/SwiftMSG.java create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java new file mode 100644 index 0000000..ff3796b --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/MT999.java @@ -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 "I" for incoming message "O" 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 "I" for incoming message "O" 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"; + } + +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java new file mode 100644 index 0000000..59b000f --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/MTConstants.java @@ -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:" // + + "(?\\w)" // + + "(?\\d{2})" // + + "(?\\w{12})" // + + "(?\\d{4})" // + + "(?\\d{5,6})" // + + "\\}" + + // Application Header Block + + "\\{2:" // + + "(?I|O)" // + + "(?\\d{3})" // + + "(\\d{10})?" // + + "(?\\w{12})" // + + "(.*)?" // + + "(?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:(?\\w{1,16})\n" // + + "(:21:(?.{1,16})\n)?" // + + ":79:(?.*?)" // + + "\n-\\}" // + // trailer + + ".*" + + ; + + static final Pattern mt999_pattern = Pattern.compile(mt999_regex); + +} + 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..9fafa6d --- /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 new file mode 100644 index 0000000..b6d971a --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java @@ -0,0 +1,275 @@ +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(); + + protected static void decodingTest(long n, String testDataEnc) { + for (long i=0; i keys = new Vector(); + Engine.decrypt_message_Return ret = pEp.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; + + public DecodingThread(long n, String testDataEnc) + { + _n = n; + _testDataEnc = testDataEnc; + } + + public void run() { + decodingTest(_n, _testDataEnc); + } + } + + private static Message encrypt(String data) { + Message m = new Message(); + m.setDir(Message.Direction.Outgoing); + m.setFrom(me); + Vector to = new Vector(); + to.add(you); + m.setTo(to); + m.setLongmsg(data); + return pEp.encrypt_message(m, null, Message.EncFormat.Inline); + } + + protected static void encodingTest(long n, String testData) { + for (long i=0; i 0 || decoding > 0)) { + System.err.println("arguments error: -d or -e (or both) must be used with a positive number"); + System.exit(1); + } + + if (deth < 1 || enth < 1 || deth > 1024 || enth > 1024) { + System.err.println("arguments error: -jd or -je must be used with a positive number (max 1024)"); + System.exit(1); + } + + me.address = MTMsgCodec.encodeBIC("232323232323"); + me.username = "sender"; + me.user_id = "23"; + pEp.myself(me); + + you.address = MTMsgCodec.encodeBIC("424242424242"); + you.username = "receiver"; + you.user_id = me.user_id; + pEp.myself(you); // make a key for it + + Message enc = encrypt(testData); + String testDataEnc = codec.encode(enc, null); + + long startTime = System.nanoTime(); + + if (decoding > 0) { + if (deth == 1) { + decodingTest(decoding, testDataEnc); + } + else { + SpeedTest st = new SpeedTest(); + Thread[] dts = new Thread[deth]; + for (int i=0; i < deth; ++i) { + dts[i] = st.new DecodingThread(decoding, testDataEnc); + dts[i].start(); + } + for (int i=0; i < deth; ++i) { + try { + dts[i].join(); + } + catch (InterruptedException ex) { } + } + } + } + + long decodingTime = System.nanoTime(); + long decodingDelta = decodingTime - startTime; + + if (encoding > 0) { + if (enth == 1) { + encodingTest(decoding, testData); + } + else { + SpeedTest st = new SpeedTest(); + Thread[] ets = new Thread[enth]; + for (int i=0; i < enth; ++i) { + ets[i] = st.new EncodingThread(encoding, testData); + ets[i].start(); + } + for (int i=0; i < enth; ++i) { + try { + ets[i].join(); + } + catch (InterruptedException ex) { } + } + } + } + + long encodingDelta = System.nanoTime() - decodingTime; + + double ent = (double) encodingDelta / 1000000000; + double det = (double) decodingDelta / 1000000000; + + double enr = (double) encoding / ent; + double der = (double) decoding / det; + + System.out.println(String.format( + "encrypted and encoded %d messages in %.3f sec. (%.1f msg./sec. per core)\n" + + "decrypted and decoded %d messages in %.3f sec. (%.1f msg./sec. per core)", + encoding, ent, enr, decoding, det, der)); + } +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SwiftMSG.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SwiftMSG.java new file mode 100644 index 0000000..268992a --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SwiftMSG.java @@ -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); + } +} diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh b/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh new file mode 100644 index 0000000..4f4fd65 --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +ENGINE_LIB_PATH=$HOME/local-default/lib + +export HOME=../resources/per-user-dirs/alice +export LD_LIBRARY_PATH=$ENGINE_LIB_PATH +export DYLD_LIBRARY_PATH=$ENGINE_LIB_PATH + +cd ../../../../../ +java -enableassertions -Xcheck:jni -cp .:../../src -Djava.library.path=.:../../src foundation.pEp.jniadapter.test.speedtest.SpeedTest $@ From 582baafef089a8fac7758581d29b633fac7aaf31 Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 30 Jul 2020 18:21:25 +0200 Subject: [PATCH 02/10] 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(); From d88e3befcf0f7b9ec8aca31c6561d5779621cca1 Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 30 Jul 2020 18:21:50 +0200 Subject: [PATCH 03/10] speedtest: adhoc Makefile --- .../pEp/jniadapter/test/speedtest/Makefile | 38 ++ .../jniadapter/test/speedtest/MtMsgCodec.java | 480 ------------------ 2 files changed, 38 insertions(+), 480 deletions(-) create mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/Makefile delete mode 100644 test/java/foundation/pEp/jniadapter/test/speedtest/MtMsgCodec.java diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/Makefile b/test/java/foundation/pEp/jniadapter/test/speedtest/Makefile new file mode 100644 index 0000000..9b9204d --- /dev/null +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/Makefile @@ -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 diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/MtMsgCodec.java b/test/java/foundation/pEp/jniadapter/test/speedtest/MtMsgCodec.java deleted file mode 100644 index 9fafa6d..0000000 --- a/test/java/foundation/pEp/jniadapter/test/speedtest/MtMsgCodec.java +++ /dev/null @@ -1,480 +0,0 @@ -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); - } -} From 25f32c536ecf6514d06188a8721aee26dfaf7c5d Mon Sep 17 00:00:00 2001 From: heck Date: Wed, 5 Aug 2020 15:44:02 +0200 Subject: [PATCH 04/10] fixing speedtest for multithreading --- .../jniadapter/test/speedtest/SpeedTest.java | 204 +++++++++--------- 1 file changed, 103 insertions(+), 101 deletions(-) diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java index d445023..6a1a4ff 100644 --- a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java @@ -6,6 +6,7 @@ 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 { @@ -14,15 +15,14 @@ public class SpeedTest { private static Identity me = new Identity(true); private static Identity you = new Identity(); - protected static void decodingTest(long n, String testDataEnc) { - for (long i=0; i keys = new Vector(); - Engine.decrypt_message_Return ret = pEp.decrypt_message(msgs[0], keys, 0); + Engine.decrypt_message_Return ret = eng.decrypt_message(msgs[0], keys, 0); String txt = ret.dst.getLongmsg(); - } - catch (ParseException ex) { + } catch (ParseException ex) { System.err.println("error: parsing test data"); System.exit(3); } @@ -32,19 +32,20 @@ public class SpeedTest { protected class DecodingThread extends Thread { private long _n; private String _testDataEnc; + private Engine _localpEp; - public DecodingThread(long n, String testDataEnc) - { + public DecodingThread(long n, String testDataEnc) { _n = n; _testDataEnc = testDataEnc; + _localpEp = new Engine(); } public void run() { - decodingTest(_n, _testDataEnc); + decodingTest(_localpEp, _n, _testDataEnc); } } - private static Message encrypt(String data) { + private static Message encrypt(Engine eng, String data) { Message m = new Message(); m.setDir(Message.Direction.Outgoing); m.setFrom(me); @@ -52,12 +53,12 @@ public class SpeedTest { to.add(you); m.setTo(to); m.setLongmsg(data); - return pEp.encrypt_message(m, null, Message.EncFormat.Inline); + return eng.encrypt_message(m, null, Message.EncFormat.Inline); } - protected static void encodingTest(long n, String testData) { - for (long i=0; i 0 || decoding > 0)) { + if (decodingCount < 0 || encodingCount < 0 || !(encodingCount > 0 || decodingCount > 0)) { System.err.println("arguments error: -d or -e (or both) must be used with a positive number"); System.exit(1); } @@ -214,65 +198,83 @@ public class SpeedTest { you.user_id = me.user_id; pEp.myself(you); // make a key for it - Message enc = encrypt(testData); + Message enc = encrypt(pEp, testData); String testDataEnc = codec.encode(enc, null); + Thread[] dts = new Thread[deth]; + Thread[] ets = new Thread[enth]; + + System.out.println("Initializing..."); + System.out.println("Creating "+ deth + " decoding threads"); + + // create threads + if (deth > 1) { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < deth; ++i) { + dts[i] = st.new DecodingThread(decodingCount, testDataEnc); + } + } + + System.out.println("Creating "+ enth + " encoding threads"); + if (enth > 1) { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < enth; ++i) { + ets[i] = st.new EncodingThread(encodingCount, testData); + } + } + + + // Benchmark starting + System.out.println("Starting benchmark..."); + System.out.println("decoding..."); long startTime = System.nanoTime(); - if (decoding > 0) { - if (deth == 1) { - decodingTest(decoding, testDataEnc); + if (deth == 1) { + decodingTest(pEp, decodingCount, testDataEnc); + } else { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < deth; ++i) { + dts[i].start(); } - else { - SpeedTest st = new SpeedTest(); - Thread[] dts = new Thread[deth]; - for (int i=0; i < deth; ++i) { - dts[i] = st.new DecodingThread(decoding, testDataEnc); - dts[i].start(); - } - for (int i=0; i < deth; ++i) { - try { - dts[i].join(); - } - catch (InterruptedException ex) { } + for (int i = 0; i < deth; ++i) { + try { + dts[i].join(); + } catch (InterruptedException ex) { } } } + long decodingTime = System.nanoTime(); long decodingDelta = decodingTime - startTime; - if (encoding > 0) { - if (enth == 1) { - encodingTest(decoding, testData); + System.out.println("encoding..."); + if (enth == 1) { + encodingTest(pEp, decodingCount, testData); + } else { + SpeedTest st = new SpeedTest(); + for (int i = 0; i < enth; ++i) { + ets[i].start(); } - else { - SpeedTest st = new SpeedTest(); - Thread[] ets = new Thread[enth]; - for (int i=0; i < enth; ++i) { - ets[i] = st.new EncodingThread(encoding, testData); - ets[i].start(); - } - for (int i=0; i < enth; ++i) { - try { - ets[i].join(); - } - catch (InterruptedException ex) { } + for (int i = 0; i < enth; ++i) { + try { + ets[i].join(); + } catch (InterruptedException ex) { } } } long encodingDelta = System.nanoTime() - decodingTime; - double ent = (double) encodingDelta / 1000000000; - double det = (double) decodingDelta / 1000000000; + double encTimeSecs = (double) encodingDelta / 1000000000; + double decTimeSecs = (double) decodingDelta / 1000000000; - double enr = (double) encoding / ent; - double der = (double) decoding / det; + double enr = (double) encodingCount * enth / encTimeSecs; + double der = (double) decodingCount * deth / decTimeSecs; System.out.println(String.format( "encrypted and encoded %d messages in %.3f sec. (%.1f msg./sec. per core)\n" + "decrypted and decoded %d messages in %.3f sec. (%.1f msg./sec. per core)", - encoding, ent, enr, decoding, det, der)); + encodingCount, encTimeSecs, enr, decodingCount, decTimeSecs, der)); } } From a6602b8ad7f62b4fd56865198711ea24cc02a9b9 Mon Sep 17 00:00:00 2001 From: heck Date: Wed, 5 Aug 2020 16:25:52 +0200 Subject: [PATCH 05/10] fixed speedtest for multithreaded benchmark --- .../{SwiftMSG.java => SWIFTMsg.java} | 0 .../jniadapter/test/speedtest/SpeedTest.java | 63 ++++++++++++------- .../jniadapter/test/speedtest/speedtest.sh | 0 3 files changed, 39 insertions(+), 24 deletions(-) rename test/java/foundation/pEp/jniadapter/test/speedtest/{SwiftMSG.java => SWIFTMsg.java} (100%) mode change 100644 => 100755 test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SwiftMSG.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SWIFTMsg.java similarity index 100% rename from test/java/foundation/pEp/jniadapter/test/speedtest/SwiftMSG.java rename to test/java/foundation/pEp/jniadapter/test/speedtest/SWIFTMsg.java diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java index 6a1a4ff..6b2f81e 100644 --- a/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java +++ b/test/java/foundation/pEp/jniadapter/test/speedtest/SpeedTest.java @@ -15,6 +15,14 @@ public class SpeedTest { 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 { @@ -79,24 +87,13 @@ public class SpeedTest { } } - public static void main(String[] args) { - long decodingCount = 0; - long encodingCount = 0; - 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(); - + 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\n" - + " -e, --encode NUMBER encode NUMBER messages\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" @@ -177,6 +174,22 @@ public class SpeedTest { System.exit(1); } } + } + + public static void main(String[] args) { + System.out.println("Initializing..."); + int cores = Runtime.getRuntime().availableProcessors(); + System.out.println(String.format("Number of cores: %d", cores)); + + encodingCount = 10; + decodingCount = 10; + enth = cores; + deth = cores; + + MT999 testMessage = new MT999("232323232323", "424242424242", "O", "23", "", "Hello, world"); + testData = testMessage.toString(); + + parseOpts(args); if (decodingCount < 0 || encodingCount < 0 || !(encodingCount > 0 || decodingCount > 0)) { System.err.println("arguments error: -d or -e (or both) must be used with a positive number"); @@ -204,8 +217,7 @@ public class SpeedTest { Thread[] dts = new Thread[deth]; Thread[] ets = new Thread[enth]; - System.out.println("Initializing..."); - System.out.println("Creating "+ deth + " decoding threads"); + System.out.println("Creating " + deth + " decoding threads"); // create threads if (deth > 1) { @@ -215,7 +227,7 @@ public class SpeedTest { } } - System.out.println("Creating "+ enth + " encoding threads"); + System.out.println("Creating " + enth + " encoding threads"); if (enth > 1) { SpeedTest st = new SpeedTest(); for (int i = 0; i < enth; ++i) { @@ -226,7 +238,7 @@ public class SpeedTest { // Benchmark starting System.out.println("Starting benchmark..."); - System.out.println("decoding..."); + System.out.println("decoding " + decodingCount + " msgs per thread"); long startTime = System.nanoTime(); if (deth == 1) { @@ -248,7 +260,7 @@ public class SpeedTest { long decodingTime = System.nanoTime(); long decodingDelta = decodingTime - startTime; - System.out.println("encoding..."); + System.out.println("encoding " + decodingCount + " msgs per thread"); if (enth == 1) { encodingTest(pEp, decodingCount, testData); } else { @@ -269,12 +281,15 @@ public class SpeedTest { double encTimeSecs = (double) encodingDelta / 1000000000; double decTimeSecs = (double) decodingDelta / 1000000000; - double enr = (double) encodingCount * enth / encTimeSecs; - double der = (double) decodingCount * deth / decTimeSecs; + long encTotal = encodingCount * enth; + long decTotal = decodingCount * deth; + + double enr = (double) encTotal / encTimeSecs; + double der = (double) decTotal / decTimeSecs; System.out.println(String.format( - "encrypted and encoded %d messages in %.3f sec. (%.1f msg./sec. per core)\n" - + "decrypted and decoded %d messages in %.3f sec. (%.1f msg./sec. per core)", - encodingCount, encTimeSecs, enr, decodingCount, decTimeSecs, der)); + "encrypted and encoded %d messages in %.3f sec. (%.1f msgs/sec) using %d threads\n" + + "decrypted and decoded %d messages in %.3f sec. (%.1f msgs/sec) using %d threads", + encTotal, encTimeSecs, enr, enth, decTotal, decTimeSecs, der, deth)); } } diff --git a/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh b/test/java/foundation/pEp/jniadapter/test/speedtest/speedtest.sh old mode 100644 new mode 100755 From 5fe48c91f5fa2bd20d65ae27c96feecc544f5d2c Mon Sep 17 00:00:00 2001 From: Hussein Kasem Date: Thu, 6 Aug 2020 20:18:15 +0200 Subject: [PATCH 06/10] JNI-114 Add PassphraseType to the passphraseCallback --- src/Makefile | 7 ++- .../pEp/jniadapter/AbstractEngine.java | 4 +- src/foundation/pEp/jniadapter/Sync.java | 2 +- ...oundation_pEp_jniadapter_AbstractEngine.cc | 58 +++++++++++++++---- src/gen_cpp_Engine.ysl2 | 4 +- src/pEp.yml2 | 6 +- src/passphrase_callback.hh | 2 +- src/passphrase_callback.hxx | 5 +- .../pEp/jniadapter/test/jni114/TestAlice.java | 3 +- 9 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/Makefile b/src/Makefile index 29ba667..e1a412b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -69,7 +69,7 @@ PEP_HEADER:=$(shell $(CXX) $(CXXFLAGS) -E -M get_header.cc | grep -oe '[^[:space .PHONY: all all: $(JAR) $(SHARED) -$(JAR): status_list.yml2 $(JAVA_SOURCES) $(C_SOURCES) +$(JAR): status_list.yml2 passphrase_status_list.yml2 $(JAVA_SOURCES) $(C_SOURCES) $(JP)/javac foundation/pEp/jniadapter/*.java $(JP)/jar cf $@ foundation/pEp/jniadapter/*.class @@ -96,6 +96,9 @@ $(SHARED): $(LIBRARY) status_list.yml2: pEp.yml2 bash ../utils/extract_pEp_status_codes_from_engine.sh "$(PEP_HEADER)" $@ +passphrase_status_list.yml2: status_list.yml2 + grep passphrase $< > $@ + foundation/pEp/jniadapter/pEpException.java: pEp.yml2 gen_java_exceptions.ysl2 pEp.yml2 $(YML2_PROC) -y gen_java_exceptions.ysl2 $< -o $@ @@ -139,6 +142,8 @@ clean: rm -f foundation/pEp/jniadapter/SyncHandshakeResult.java rm -f foundation/pEp/jniadapter/SyncHandshakeSignal.java rm -f foundation/pEp/jniadapter/CipherSuite.java + rm -f foundation/pEp/jniadapter/PassphraseType.java rm -f throw_pEp_exception.* rm -f foundation_pEp_jniadapter_Message.cc foundation_pEp_jniadapter_Engine.cc rm -f status_list.yml2 + rm -f passphrase_status_list.yml2 diff --git a/src/foundation/pEp/jniadapter/AbstractEngine.java b/src/foundation/pEp/jniadapter/AbstractEngine.java index 92adbd2..cb56de8 100644 --- a/src/foundation/pEp/jniadapter/AbstractEngine.java +++ b/src/foundation/pEp/jniadapter/AbstractEngine.java @@ -193,11 +193,11 @@ abstract class AbstractEngine extends UniquelyIdentifiable implements AutoClosea return 0; } - public byte[] passphraseRequiredFromC() { + public byte[] passphraseRequiredFromC(final PassphraseType passphraseType) { String ret = ""; if (passphraseRequiredCallback != null) { System.out.println("calling passphraseRequiredCallback on engine ObjID:" + getId()); - ret = passphraseRequiredCallback.passphraseRequired(); + ret = passphraseRequiredCallback.passphraseRequired(passphraseType); } else { System.out.println("no callback registered on engine ObjID:" + getId()); // if this happens (no callback registered diff --git a/src/foundation/pEp/jniadapter/Sync.java b/src/foundation/pEp/jniadapter/Sync.java index e83211e..7593477 100644 --- a/src/foundation/pEp/jniadapter/Sync.java +++ b/src/foundation/pEp/jniadapter/Sync.java @@ -20,7 +20,7 @@ public interface Sync { } interface PassphraseRequiredCallback { - String passphraseRequired(); + String passphraseRequired(final PassphraseType pEpStatus); } public class DefaultCallback diff --git a/src/foundation_pEp_jniadapter_AbstractEngine.cc b/src/foundation_pEp_jniadapter_AbstractEngine.cc index ce5edff..026e18b 100644 --- a/src/foundation_pEp_jniadapter_AbstractEngine.cc +++ b/src/foundation_pEp_jniadapter_AbstractEngine.cc @@ -21,20 +21,24 @@ JavaVM *jvm= nullptr; std::mutex mutex_obj; -jfieldID field_value = nullptr; +jfieldID signal_field_value = nullptr; +jfieldID passphrase_type_field_value = nullptr; jmethodID messageConstructorMethodID = nullptr; jmethodID messageToSendMethodID = nullptr; jmethodID notifyHandShakeMethodID = nullptr; jmethodID needsFastPollMethodID = nullptr; jmethodID passphraseRequiredMethodID = nullptr; -jmethodID method_values = nullptr; +jmethodID sync_handshake_signal_values = nullptr; +jmethodID passphrase_status_values = nullptr; +jmethodID passphrase_callback_values = nullptr; jobject objj = nullptr; jclass messageClass = nullptr; -jclass identityClass = nullptr;; +jclass identityClass = nullptr; jclass signalClass = nullptr; jclass engineClass = nullptr; +jclass passphraseTypeClass = nullptr; namespace JNISync { JNIEnv* env() { @@ -72,6 +76,8 @@ void jni_init() { _env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/_Identity"))); signalClass = reinterpret_cast( _env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/SyncHandshakeSignal"))); + passphraseTypeClass = reinterpret_cast( + _env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/PassphraseType"))); engineClass = reinterpret_cast(_env->NewGlobalRef(findClass(_env, "foundation/pEp/jniadapter/Engine"))); messageConstructorMethodID = _env->GetMethodID(messageClass, "", "(J)V"); @@ -90,19 +96,47 @@ void jni_init() { passphraseRequiredMethodID = _env->GetMethodID( engineClass, "passphraseRequiredFromC", - "()[B"); + "(Lfoundation/pEp/jniadapter/PassphraseType;)[B"); - method_values = JNISync::env()->GetStaticMethodID(signalClass, "values", + sync_handshake_signal_values = JNISync::env()->GetStaticMethodID(signalClass, "values", "()[Lfoundation/pEp/jniadapter/SyncHandshakeSignal;"); - field_value = JNISync::env()->GetFieldID(signalClass, "value", "I"); + passphrase_status_values = JNISync::env()->GetStaticMethodID(passphraseTypeClass, "values", + "()[Lfoundation/pEp/jniadapter/PassphraseType;"); + signal_field_value = JNISync::env()->GetFieldID(signalClass, "value", "I"); + passphrase_type_field_value = JNISync::env()->GetFieldID(passphraseTypeClass, "value", "I"); } -char* JNIAdapter::passphraseRequiredCallback() { +char* JNIAdapter::passphraseRequiredCallback(const PEP_STATUS status) { pEpLog("called"); + jobject status_ = nullptr; + { + assert(passphraseTypeClass); + assert(passphrase_status_values); + assert(passphrase_type_field_value); + + jobjectArray values = (jobjectArray) JNISync::env()->CallStaticObjectMethod(passphraseTypeClass, + passphrase_status_values); + + if (JNISync::env()->ExceptionCheck()) { + JNISync::env()->ExceptionClear(); + throw_pEp_Exception(JNISync::env(), PEP_UNKNOWN_ERROR); + } + jsize values_size = JNISync::env()->GetArrayLength(values); + for (jsize i = 0; i < values_size; i++) { + jobject element = JNISync::env()->GetObjectArrayElement(values, i); + assert(element); + jint value = JNISync::env()->GetIntField(element, passphrase_type_field_value); + if (value == (jint) status) { + status_ = element; + break; + } + JNISync::env() -> DeleteLocalRef(element); + } + } assert(objj && passphraseRequiredMethodID); - jobject ppJO = JNISync::env()->CallObjectMethod(objj, passphraseRequiredMethodID); + jobject ppJO = JNISync::env()->CallObjectMethod(objj, passphraseRequiredMethodID, status_); if (JNISync::env()->ExceptionCheck()) { JNISync::env()->ExceptionDescribe(); JNISync::env()->ExceptionClear(); @@ -160,11 +194,11 @@ PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handsha jobject signal_ = nullptr; { assert(signalClass); - assert(method_values); - assert(field_value); + assert(sync_handshake_signal_values); + assert(signal_field_value); jobjectArray values = (jobjectArray) JNISync::env()->CallStaticObjectMethod(signalClass, - method_values); + sync_handshake_signal_values); if (JNISync::env()->ExceptionCheck()) { JNISync::env()->ExceptionClear(); return PEP_UNKNOWN_ERROR; @@ -174,7 +208,7 @@ PEP_STATUS notifyHandshake(pEp_identity *me, pEp_identity *partner, sync_handsha for (jsize i = 0; i < values_size; i++) { jobject element = JNISync::env()->GetObjectArrayElement(values, i); assert(element); - jint value = JNISync::env()->GetIntField(element, field_value); + jint value = JNISync::env()->GetIntField(element, signal_field_value); if (value == (jint) signal) { signal_ = element; break; diff --git a/src/gen_cpp_Engine.ysl2 b/src/gen_cpp_Engine.ysl2 index 3e52f11..d4e2e90 100644 --- a/src/gen_cpp_Engine.ysl2 +++ b/src/gen_cpp_Engine.ysl2 @@ -80,11 +80,11 @@ tstylesheet { pEpLog("calling passphrase_cache.api(::«@name»())"); status = passphrase_cache.api(::«@name»,session()`apply "parm", mode=call`); pEpLog("PEP_STATUS:" << status); - if(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE ) { + if(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE || status == PEP_PASSPHRASE_FOR_NEW_KEYS_REQUIRED) { pEpLog("none of the cached passphrases worked"); if(retryCount < maxRetries) { // call the app - char* _passphrase = passphraseRequiredCallback(); + char* _passphrase = passphraseRequiredCallback(status); pEpLog("callback returned, config_passphrase() with new passphrase"); PEP_STATUS status = ::config_passphrase(session(),passphrase_cache.add(_passphrase)); retryAgain = true; diff --git a/src/pEp.yml2 b/src/pEp.yml2 index c3022fa..d446b3a 100644 --- a/src/pEp.yml2 +++ b/src/pEp.yml2 @@ -10,7 +10,11 @@ namespace pEp { exception Status { include ./status_list.yml2 }; - + + enum PassphraseType { + include ./passphrase_status_list.yml2 + }; + enum Color { PEP_color_no_color > 0 PEP_color_yellow > 1 diff --git a/src/passphrase_callback.hh b/src/passphrase_callback.hh index 8482077..30808dd 100644 --- a/src/passphrase_callback.hh +++ b/src/passphrase_callback.hh @@ -5,7 +5,7 @@ namespace pEp { namespace JNIAdapter { - char* passphraseRequiredCallback(); + char* passphraseRequiredCallback(const PEP_STATUS status); template PEP_STATUS passphraseWrap( PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); diff --git a/src/passphrase_callback.hxx b/src/passphrase_callback.hxx index 20bfa36..63603f3 100644 --- a/src/passphrase_callback.hxx +++ b/src/passphrase_callback.hxx @@ -17,11 +17,12 @@ namespace pEp { pEpLog("calling passphrase_cache.api from basic_api"); status = passphrase_cache.api(f, session, a...); pEpLog("PEP_STATUS:" << status); - if (status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE) { + if (status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE + || status == PEP_PASSPHRASE_FOR_NEW_KEYS_REQUIRED) { pEpLog("none of the cached passphrases worked"); if (retryCount < maxRetries) { // call the app - char *_passphrase = passphraseRequiredCallback(); + char *_passphrase = passphraseRequiredCallback(status); pEpLog("callback returned, config_passphrase() with new passphrase"); PEP_STATUS status = ::config_passphrase(session, passphrase_cache.add(_passphrase)); diff --git a/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java b/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java index cc09858..e6a753a 100644 --- a/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java +++ b/test/java/foundation/pEp/jniadapter/test/jni114/TestAlice.java @@ -50,10 +50,11 @@ class TestAlice { // Register callback passphraseRequired() ctx.engine.setPassphraseRequiredCallback(new Sync.PassphraseRequiredCallback() { @Override - public String passphraseRequired() { + public String passphraseRequired(PassphraseType type) { log("passphraseRequired() called"); log("Please Enter Passphrase..."); sleep(2000); + assert type == PassphraseType.pEpPassphraseRequired; return "passphrase_alice"; } }); From 438d1a62322eeac3f4a47ecb774cdf05bef5a22b Mon Sep 17 00:00:00 2001 From: heck Date: Thu, 13 Aug 2020 00:38:03 +0200 Subject: [PATCH 07/10] Add comipler optimization -O3 to release build --- Makefile.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.conf b/Makefile.conf index 15c9dde..e9f2627 100644 --- a/Makefile.conf +++ b/Makefile.conf @@ -62,7 +62,7 @@ ifeq ($(DEBUG),1) CXXFLAGS+=-g else $(info Release Build (set DEBUG=1 for debug build)) - CXXFLAGS+=-DNDEBUG=1 + CXXFLAGS+=-DNDEBUG=1 -O3 endif ### YML_PATH is needed in the environment of every call to a program of the YML2 distribution From 5f85c064a1fee3440ec4a23d44acee603ba97e8a Mon Sep 17 00:00:00 2001 From: heck Date: Tue, 18 Aug 2020 21:29:09 +0200 Subject: [PATCH 08/10] just cosmetics --- .hgignore | 1 + src/foundation_pEp_jniadapter_AbstractEngine.cc | 2 +- src/passphrase_callback.hh | 9 ++++----- src/passphrase_callback.hxx | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.hgignore b/.hgignore index d83d981..e81e918 100644 --- a/.hgignore +++ b/.hgignore @@ -39,6 +39,7 @@ IdentityFlags.java SyncHandshakeSignal.java CipherSuite.java +passphrase_status_list.yml2 status_list.yml2 ndk*/ diff --git a/src/foundation_pEp_jniadapter_AbstractEngine.cc b/src/foundation_pEp_jniadapter_AbstractEngine.cc index 026e18b..e5714ba 100644 --- a/src/foundation_pEp_jniadapter_AbstractEngine.cc +++ b/src/foundation_pEp_jniadapter_AbstractEngine.cc @@ -131,7 +131,7 @@ char* JNIAdapter::passphraseRequiredCallback(const PEP_STATUS status) { status_ = element; break; } - JNISync::env() -> DeleteLocalRef(element); + JNISync::env()->DeleteLocalRef(element); } } assert(objj && passphraseRequiredMethodID); diff --git a/src/passphrase_callback.hh b/src/passphrase_callback.hh index 30808dd..e0f9804 100644 --- a/src/passphrase_callback.hh +++ b/src/passphrase_callback.hh @@ -5,11 +5,10 @@ namespace pEp { namespace JNIAdapter { - char* passphraseRequiredCallback(const PEP_STATUS status); + char* passphraseRequiredCallback(const PEP_STATUS status); - template PEP_STATUS passphraseWrap( - PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); - }; -}; + template PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a); + } +} #include "passphrase_callback.hxx" \ No newline at end of file diff --git a/src/passphrase_callback.hxx b/src/passphrase_callback.hxx index 63603f3..e812841 100644 --- a/src/passphrase_callback.hxx +++ b/src/passphrase_callback.hxx @@ -5,8 +5,7 @@ namespace pEp { namespace JNIAdapter { - template PEP_STATUS passphraseWrap( - PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a) { + template PEP_STATUS passphraseWrap(PEP_STATUS f(PEP_SESSION, A...), PEP_SESSION session, A... a) { pEpLog("cached passphrase mode"); bool retryAgain = false; int maxRetries = 3; @@ -17,15 +16,16 @@ namespace pEp { pEpLog("calling passphrase_cache.api from basic_api"); status = passphrase_cache.api(f, session, a...); pEpLog("PEP_STATUS:" << status); - if (status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE - || status == PEP_PASSPHRASE_FOR_NEW_KEYS_REQUIRED) { + if (status == PEP_PASSPHRASE_REQUIRED || + status == PEP_WRONG_PASSPHRASE || + status == PEP_PASSPHRASE_FOR_NEW_KEYS_REQUIRED) + { pEpLog("none of the cached passphrases worked"); if (retryCount < maxRetries) { // call the app char *_passphrase = passphraseRequiredCallback(status); pEpLog("callback returned, config_passphrase() with new passphrase"); - PEP_STATUS status = ::config_passphrase(session, - passphrase_cache.add(_passphrase)); + PEP_STATUS status = ::config_passphrase(session, passphrase_cache.add(_passphrase)); retryAgain = true; retryCount++; } else { From 476011d7744f8f19e425e6e50f61f92f958cd326 Mon Sep 17 00:00:00 2001 From: heck Date: Tue, 18 Aug 2020 21:57:06 +0200 Subject: [PATCH 10/10] use passphraseWrap() in CodeGen template too. --- ...oundation_pEp_jniadapter_AbstractEngine.cc | 1 - src/gen_cpp_Engine.ysl2 | 30 ++----------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/src/foundation_pEp_jniadapter_AbstractEngine.cc b/src/foundation_pEp_jniadapter_AbstractEngine.cc index e5714ba..624b233 100644 --- a/src/foundation_pEp_jniadapter_AbstractEngine.cc +++ b/src/foundation_pEp_jniadapter_AbstractEngine.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/src/gen_cpp_Engine.ysl2 b/src/gen_cpp_Engine.ysl2 index d4e2e90..466e0db 100644 --- a/src/gen_cpp_Engine.ysl2 +++ b/src/gen_cpp_Engine.ysl2 @@ -9,6 +9,7 @@ tstylesheet { template "interface" document("foundation_pEp_jniadapter_{@name}.cc", "text") || + #include #include #include #include @@ -70,34 +71,7 @@ tstylesheet { choose { when "@cached = 'true'" { || - pEpLog("cached passphrase mode"); - bool retryAgain = false; - int maxRetries = 3; - int retryCount = 0; - PEP_STATUS status; - do { - // the actual target function - pEpLog("calling passphrase_cache.api(::«@name»())"); - status = passphrase_cache.api(::«@name»,session()`apply "parm", mode=call`); - pEpLog("PEP_STATUS:" << status); - if(status == PEP_PASSPHRASE_REQUIRED || status == PEP_WRONG_PASSPHRASE || status == PEP_PASSPHRASE_FOR_NEW_KEYS_REQUIRED) { - pEpLog("none of the cached passphrases worked"); - if(retryCount < maxRetries) { - // call the app - char* _passphrase = passphraseRequiredCallback(status); - pEpLog("callback returned, config_passphrase() with new passphrase"); - PEP_STATUS status = ::config_passphrase(session(),passphrase_cache.add(_passphrase)); - retryAgain = true; - retryCount++; - } else { - pEpLog("max retries reached:" << maxRetries); - retryAgain = false; - } - } else { - retryAgain = false; - } - } while (retryAgain); - + PEP_STATUS status = passphraseWrap(::«@name», session()`apply "parm", mode=call`); || } otherwise { ||